main.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. /* Toybox infrastructure.
  2. *
  3. * Copyright 2006 Rob Landley <[email protected]>
  4. */
  5. #include "toys.h"
  6. // Populate toy_list[].
  7. #undef NEWTOY
  8. #undef OLDTOY
  9. #define NEWTOY(name, opts, flags) {#name, name##_main, OPTSTR_##name, flags},
  10. #define OLDTOY(name, oldname, flags) \
  11. {#name, oldname##_main, OPTSTR_##oldname, flags},
  12. struct toy_list toy_list[] = {
  13. #include "generated/newtoys.h"
  14. };
  15. // global context for this command.
  16. struct toy_context toys;
  17. union global_union this;
  18. char *toybox_version = TOYBOX_VERSION, toybuf[4096], libbuf[4096];
  19. struct toy_list *toy_find(char *name)
  20. {
  21. int top, bottom, middle;
  22. if (!CFG_TOYBOX || strchr(name, '/')) return 0;
  23. // Multiplexer name works as prefix, else skip first entry (it's out of order)
  24. if (!toys.which && strstart(&name, toy_list->name)) return toy_list;
  25. bottom = 1;
  26. // Binary search to find this command.
  27. top = ARRAY_LEN(toy_list)-1;
  28. for (;;) {
  29. int result;
  30. middle = (top+bottom)/2;
  31. if (middle<bottom || middle>top) return 0;
  32. result = strcmp(name,toy_list[middle].name);
  33. if (!result) return toy_list+middle;
  34. if (result<0) top = --middle;
  35. else bottom = ++middle;
  36. }
  37. }
  38. // Figure out whether or not anything is using the option parsing logic,
  39. // because the compiler can't figure out whether or not to optimize it away
  40. // on its' own. NEED_OPTIONS becomes a constant allowing if() to optimize
  41. // stuff out via dead code elimination.
  42. #undef NEWTOY
  43. #undef OLDTOY
  44. #define NEWTOY(name, opts, flags) opts ||
  45. #define OLDTOY(name, oldname, flags) OPTSTR_##oldname ||
  46. static const int NEED_OPTIONS =
  47. #include "generated/newtoys.h"
  48. 0; // Ends the opts || opts || opts...
  49. // Same trick but with the TRIMHELP plumbing.
  50. #undef NEWTOY
  51. #undef OLDTOY
  52. #define NEWTOY(name, opts, flags) ((flags)&TOYFLAG_TRIMHELP) ||
  53. #define OLDTOY(name, oldname, flags) ((flags)&TOYFLAG_TRIMHELP) ||
  54. static const int NEED_TRIMHELP =
  55. #include "generated/newtoys.h"
  56. 0;
  57. // Populate help text array
  58. #undef NEWTOY
  59. #undef OLDTOY
  60. #define NEWTOY(name,opt,flags) HELP_##name "\0"
  61. #if CFG_TOYBOX
  62. #define OLDTOY(name,oldname,flags) "\xff" #oldname "\0"
  63. #else
  64. #define OLDTOY(name, oldname, flags) HELP_##oldname "\0"
  65. #endif
  66. #if CFG_TOYBOX_ZHELP
  67. #include "generated/zhelp.h"
  68. static char *help_data = 0;
  69. #else
  70. #include "generated/help.h"
  71. static const char help_array[] =
  72. #include "generated/newtoys.h"
  73. ;
  74. static char *help_data = (void *)help_array;
  75. #define zhelp_data 0
  76. #define ZHELP_LEN 0
  77. #endif
  78. void show_help(int flags)
  79. {
  80. int i = toys.which-toy_list;
  81. char *s, *ss;
  82. if (!CFG_TOYBOX_HELP) return;
  83. if (CFG_TOYBOX_ZHELP)
  84. gunzip_mem(zhelp_data, sizeof(zhelp_data), help_data = xmalloc(ZHELP_LEN),
  85. ZHELP_LEN);
  86. if (flags&HELP_HEADER)
  87. printf("Toybox %s"USE_TOYBOX(" multicall binary")"%s\n\n",
  88. toybox_version, (CFG_TOYBOX && i) ? " (see toybox --help)"
  89. : " (see https://landley.net/toybox)");
  90. for (;;) {
  91. s = (void *)help_data;
  92. while (i--) s += strlen(s) + 1;
  93. // If it's an alias, restart search for real name
  94. if (*s != 255) break;
  95. i = toy_find(++s)-toy_list;
  96. if ((flags & HELP_SEE) && toy_list[i].flags) {
  97. if (flags & HELP_HTML) printf("See <a href=#%s>%s</a>\n", s, s);
  98. else printf("%s see %s\n", toys.which->name, s);
  99. return;
  100. }
  101. }
  102. // Only "help -u" calls HELP_USAGE
  103. if (CFG_HELP && (flags&HELP_USAGE)) {
  104. strstart(&s, "usage: ");
  105. for (ss = s; *ss && *ss!='\n'; ss++);
  106. printf("%.*s\n", (int)(ss-s), s);
  107. } else if (!NEED_TRIMHELP || !(toys.which->flags&TOYFLAG_TRIMHELP)) puts(s);
  108. // TRIMHELP lines starting with ! are only displayed with BIGHELP,
  109. // and the starting ! is edited out either way.
  110. else {
  111. int big = toys.which->flags&TOYFLAG_BIGHELP, usage = 1;
  112. for (; *s; s++) {
  113. // For usage: line, chop out individual chars after each !
  114. if (usage && *s=='!') {
  115. s++;
  116. if (!big) continue;
  117. }
  118. putchar(*s);
  119. // For other lines, chop out whole lines starting with !
  120. if (*s=='\n') {
  121. usage = 0;
  122. if (s[1]=='!') {
  123. s++;
  124. if (big) continue;
  125. s += strcspn(s, "\n");
  126. if (!*s) return;
  127. }
  128. }
  129. }
  130. putchar('\n');
  131. }
  132. }
  133. static void unknown(char *name)
  134. {
  135. toys.exitval = 127;
  136. toys.which = toy_list;
  137. help_exit("Unknown command %s", name);
  138. }
  139. // Parse --help and --version for (almost) all commands
  140. void check_help(char **arg)
  141. {
  142. long flags = toys.which->flags;
  143. if (!CFG_TOYBOX_HELP_DASHDASH || !*arg) return;
  144. if (!CFG_TOYBOX || toys.which!=toy_list) if (flags&TOYFLAG_NOHELP) return;
  145. if (!strcmp(*arg, "--help")) {
  146. if (CFG_TOYBOX && toys.which == toy_list && arg[1]) {
  147. toys.which = 0;
  148. if (!(toys.which = toy_find(arg[1]))) unknown(arg[1]);
  149. }
  150. show_help(HELP_HEADER);
  151. xexit();
  152. }
  153. if (!strcmp(*arg, "--version")) {
  154. // Lie to autoconf when it asks stupid questions, so configure regexes
  155. // that look for "GNU sed version %f" greater than some old buggy number
  156. // don't fail us for not matching their narrow expectations.
  157. *toybuf = 0;
  158. if (flags&TOYFLAG_AUTOCONF)
  159. sprintf(toybuf, " (is not GNU %s 9.0)", toys.which->name);
  160. xprintf("toybox %s%s\n", toybox_version, toybuf);
  161. xexit();
  162. }
  163. }
  164. // Setup toybox global state for this command.
  165. void toy_singleinit(struct toy_list *which, char *argv[])
  166. {
  167. toys.which = which;
  168. toys.argv = argv;
  169. toys.toycount = ARRAY_LEN(toy_list);
  170. if (NEED_OPTIONS && which->options) get_optflags();
  171. else {
  172. check_help(toys.optargs = argv+1);
  173. for (toys.optc = 0; toys.optargs[toys.optc]; toys.optc++);
  174. }
  175. // Setup we only want to do once: skip for multiplexer or NOFORK reentry
  176. if (!(CFG_TOYBOX && which == toy_list) && !(which->flags & TOYFLAG_NOFORK)) {
  177. char *buf = 0;
  178. int btype = _IOFBF;
  179. toys.old_umask = umask(0);
  180. if (!(which->flags & TOYFLAG_UMASK)) umask(toys.old_umask);
  181. // Try user's locale, but if that isn't UTF-8 merge in a UTF-8 locale's
  182. // character type data. (Fall back to en_US for MacOS.)
  183. setlocale(LC_CTYPE, "");
  184. if (strcmp("UTF-8", nl_langinfo(CODESET)))
  185. uselocale(newlocale(LC_CTYPE_MASK, "C.UTF-8", 0) ? :
  186. newlocale(LC_CTYPE_MASK, "en_US.UTF-8", 0));
  187. if (which->flags & TOYFLAG_LINEBUF) btype = _IOLBF;
  188. else if (which->flags & TOYFLAG_NOBUF) btype = _IONBF;
  189. else buf = xmalloc(4096);
  190. setvbuf(stdout, buf, btype, buf ? 4096 : 0);
  191. }
  192. }
  193. // Full init needed by multiplexer or reentrant calls, calls singleinit at end
  194. void toy_init(struct toy_list *which, char *argv[])
  195. {
  196. void *oldwhich = toys.which;
  197. // Drop permissions for non-suid commands.
  198. if (CFG_TOYBOX_SUID) {
  199. if (!toys.which) toys.which = toy_list;
  200. uid_t uid = getuid(), euid = geteuid();
  201. if (!(which->flags & TOYFLAG_STAYROOT)) {
  202. if (uid != euid) {
  203. if (setuid(uid)) perror_exit("setuid %d->%d", euid, uid); // drop root
  204. euid = uid;
  205. toys.wasroot++;
  206. }
  207. } else if (CFG_TOYBOX_DEBUG && uid && which != toy_list)
  208. error_msg("Not installed suid root");
  209. if ((which->flags & TOYFLAG_NEEDROOT) && euid) {
  210. toys.which = which;
  211. check_help(argv+1);
  212. help_exit("Not root");
  213. }
  214. }
  215. memset(&toys, 0, offsetof(struct toy_context, rebound));
  216. if (oldwhich) memset(&this, 0, sizeof(this));
  217. // Continue to portion of init needed by standalone commands
  218. toy_singleinit(which, argv);
  219. }
  220. // Run an internal toybox command.
  221. // Only returns if it can't run command internally, otherwise xexit() when done.
  222. void toy_exec_which(struct toy_list *which, char *argv[])
  223. {
  224. // Return if we can't find it (which includes no multiplexer case),
  225. if (!which || (which->flags&TOYFLAG_NOFORK)) return;
  226. // Return if stack depth getting noticeable (proxy for leaked heap, etc).
  227. // Compiler writers have decided subtracting char * is undefined behavior,
  228. // so convert to integers. (LP64 says sizeof(long)==sizeof(pointer).)
  229. // Signed typecast so stack growth direction is irrelevant: we're measuring
  230. // the distance between two pointers on the same stack, hence the labs().
  231. if (!CFG_TOYBOX_NORECURSE && toys.stacktop) {
  232. int i;
  233. if (labs((long)toys.stacktop-(long)&which)>24000) return;
  234. for (i = 0; i<NSIG; i++) signal(i, SIG_DFL);
  235. }
  236. // Return if we need to re-exec to acquire root via suid bit.
  237. if (toys.which && (which->flags&TOYFLAG_ROOTONLY) && toys.wasroot) return;
  238. // Run command
  239. toy_init(which, argv);
  240. if (toys.which) toys.which->toy_main();
  241. xexit();
  242. }
  243. // Lookup internal toybox command to run via argv[0]
  244. void toy_exec(char *argv[])
  245. {
  246. toy_exec_which(toy_find(*argv), argv);
  247. }
  248. // Multiplexer command, first argument is command to run, rest are args to that.
  249. // If first argument starts with - output list of command install paths.
  250. void toybox_main(void)
  251. {
  252. char *toy_paths[] = {"usr/", "bin/", "sbin/", 0}, *s = toys.argv[1];
  253. int i, len = 0;
  254. unsigned width = 80;
  255. // fast path: try to exec immediately.
  256. // (Leave toys.which null to disable suid return logic.)
  257. // Try dereferencing symlinks until we hit a recognized name
  258. while (s) {
  259. char *ss = basename(s);
  260. struct toy_list *tl = toy_find(ss);
  261. if (tl==toy_list && s!=toys.argv[1]) unknown(ss);
  262. toy_exec_which(tl, toys.argv+1);
  263. s = (0<readlink(s, libbuf, sizeof(libbuf))) ? libbuf : 0;
  264. }
  265. // For early error reporting
  266. toys.which = toy_list;
  267. if (toys.argv[1] && strcmp(toys.argv[1], "--long")) unknown(toys.argv[1]);
  268. // Output list of commands.
  269. terminal_size(&width, 0);
  270. for (i = 1; i<ARRAY_LEN(toy_list); i++) {
  271. int fl = toy_list[i].flags;
  272. if (fl & TOYMASK_LOCATION) {
  273. if (toys.argv[1]) {
  274. int j;
  275. for (j = 0; toy_paths[j]; j++)
  276. if (fl & (1<<j)) len += printf("%s", toy_paths[j]);
  277. }
  278. len += printf("%s",toy_list[i].name);
  279. if (++len > width-15) len = 0;
  280. xputc(len ? ' ' : '\n');
  281. }
  282. }
  283. xputc('\n');
  284. }
  285. int main(int argc, char *argv[])
  286. {
  287. // don't segfault if our environment is crazy
  288. // TODO mooted by kernel commit dcd46d897adb7 5.17 kernel Jan 2022
  289. if (!*argv) return 127;
  290. // Snapshot stack location so we can detect recursion depth later.
  291. // Nommu has special reentry path, !stacktop = "vfork/exec self happened"
  292. if (!CFG_TOYBOX_FORK && (0x80 & **argv)) **argv &= 0x7f;
  293. else {
  294. int stack_start; // here so probe var won't permanently eat stack
  295. toys.stacktop = &stack_start;
  296. }
  297. if (CFG_TOYBOX) {
  298. // Call the multiplexer with argv[] as its arguments so it can toy_find()
  299. toys.argv = argv-1;
  300. toybox_main();
  301. } else {
  302. // single command built standalone with no multiplexer is first list entry
  303. toy_singleinit(toy_list, argv);
  304. toy_list->toy_main();
  305. }
  306. xexit();
  307. }