1 /* Toybox infrastructure. 2 * 3 * Copyright 2006 Rob Landley <rob@landley.net> 4 */ 5 6 #include "toys.h" 7 8 // Populate toy_list[]. 9 10 #undef NEWTOY 11 #undef OLDTOY 12 #define NEWTOY(name, opts, flags) {#name, name##_main, OPTSTR_##name, flags}, 13 #define OLDTOY(name, oldname, flags) \ 14 {#name, oldname##_main, OPTSTR_##oldname, flags}, 15 16 struct toy_list toy_list[] = { 17 #include "generated/newtoys.h" 18 }; 19 20 // global context for this command. 21 22 struct toy_context toys; 23 union global_union this; 24 char toybuf[4096], libbuf[4096]; 25 26 struct toy_list *toy_find(char *name) 27 { 28 int top, bottom, middle; 29 30 if (!CFG_TOYBOX || strchr(name, '/')) return 0; 31 32 // Multiplexer name works as prefix, else skip first entry (it's out of order) 33 if (!toys.which && strstart(&name, "toybox")) return toy_list; 34 bottom = 1; 35 36 // Binary search to find this command. 37 top = ARRAY_LEN(toy_list)-1; 38 for (;;) { 39 int result; 40 41 middle = (top+bottom)/2; 42 if (middle<bottom || middle>top) return 0; 43 result = strcmp(name,toy_list[middle].name); 44 if (!result) return toy_list+middle; 45 if (result<0) top = --middle; 46 else bottom = ++middle; 47 } 48 } 49 50 // Figure out whether or not anything is using the option parsing logic, 51 // because the compiler can't figure out whether or not to optimize it away 52 // on its' own. NEED_OPTIONS becomes a constant allowing if() to optimize 53 // stuff out via dead code elimination. 54 55 #undef NEWTOY 56 #undef OLDTOY 57 #define NEWTOY(name, opts, flags) opts || 58 #define OLDTOY(name, oldname, flags) OPTSTR_##oldname || 59 static const int NEED_OPTIONS = 60 #include "generated/newtoys.h" 61 0; // Ends the opts || opts || opts... 62 63 static void unknown(char *name) 64 { 65 toys.exitval = 127; 66 toys.which = toy_list; 67 error_exit("Unknown command %s", name); 68 } 69 70 // Setup toybox global state for this command. 71 static void toy_singleinit(struct toy_list *which, char *argv[]) 72 { 73 toys.which = which; 74 toys.argv = argv; 75 toys.toycount = ARRAY_LEN(toy_list); 76 77 // Parse --help and --version for (almost) all commands 78 if (CFG_TOYBOX_HELP_DASHDASH && !(which->flags & TOYFLAG_NOHELP) && argv[1]) { 79 if (!strcmp(argv[1], "--help")) { 80 if (CFG_TOYBOX && toys.which == toy_list && toys.argv[2]) 81 if (!(toys.which = toy_find(toys.argv[2]))) unknown(toys.argv[2]); 82 show_help(stdout, 1); 83 xexit(); 84 } 85 86 if (!strcmp(argv[1], "--version")) { 87 xputs("toybox "TOYBOX_VERSION); 88 xexit(); 89 } 90 } 91 92 if (NEED_OPTIONS && which->options) get_optflags(); 93 else { 94 toys.optargs = argv+1; 95 for (toys.optc = 0; toys.optargs[toys.optc]; toys.optc++); 96 } 97 98 if (!(which->flags & TOYFLAG_NOFORK)) { 99 toys.old_umask = umask(0); 100 if (!(which->flags & TOYFLAG_UMASK)) umask(toys.old_umask); 101 if (CFG_TOYBOX_I18N) { 102 // Deliberately try C.UTF-8 before the user's locale to work around users 103 // that choose non-UTF-8 locales. macOS doesn't support C.UTF-8 though. 104 if (!setlocale(LC_CTYPE, "C.UTF-8")) setlocale(LC_CTYPE, ""); 105 } 106 setlinebuf(stdout); 107 } 108 } 109 110 // Full init needed by multiplexer or reentrant calls, calls singleinit at end 111 void toy_init(struct toy_list *which, char *argv[]) 112 { 113 void *oldwhich = toys.which; 114 115 // Drop permissions for non-suid commands. 116 117 if (CFG_TOYBOX_SUID) { 118 if (!toys.which) toys.which = toy_list; 119 120 uid_t uid = getuid(), euid = geteuid(); 121 122 if (!(which->flags & TOYFLAG_STAYROOT)) { 123 if (uid != euid) { 124 if (setuid(uid)) perror_exit("setuid %d->%d", euid, uid); // drop root 125 euid = uid; 126 toys.wasroot++; 127 } 128 } else if (CFG_TOYBOX_DEBUG && uid && which != toy_list) 129 error_msg("Not installed suid root"); 130 131 if ((which->flags & TOYFLAG_NEEDROOT) && euid) help_exit("Not root"); 132 } 133 134 // Free old toys contents (to be reentrant), but leave rebound if any 135 // don't blank old optargs if our new argc lives in the old optargs. 136 if (argv<toys.optargs || argv>toys.optargs+toys.optc) free(toys.optargs); 137 memset(&toys, 0, offsetof(struct toy_context, rebound)); 138 if (oldwhich) memset(&this, 0, sizeof(this)); 139 140 // Continue to portion of init needed by standalone commands 141 toy_singleinit(which, argv); 142 } 143 144 // Run an internal toybox command. 145 // Only returns if it can't run command internally, otherwise xexit() when done. 146 void toy_exec_which(struct toy_list *which, char *argv[]) 147 { 148 // Return if we can't find it (which includes no multiplexer case), 149 if (!which) return; 150 151 // Return if stack depth getting noticeable (proxy for leaked heap, etc). 152 153 // Compiler writers have decided subtracting char * is undefined behavior, 154 // so convert to integers. (LP64 says sizeof(long)==sizeof(pointer).) 155 // Signed typecast so stack growth direction is irrelevant: we're measuring 156 // the distance between two pointers on the same stack, hence the labs(). 157 if (!CFG_TOYBOX_NORECURSE && toys.stacktop) 158 if (labs((long)toys.stacktop-(long)&which)>6000) return; 159 160 // Return if we need to re-exec to acquire root via suid bit. 161 if (toys.which && (which->flags&TOYFLAG_ROOTONLY) && toys.wasroot) return; 162 163 // Run command 164 toy_init(which, argv); 165 if (toys.which) toys.which->toy_main(); 166 xexit(); 167 } 168 169 // Lookup internal toybox command to run via argv[0] 170 void toy_exec(char *argv[]) 171 { 172 toy_exec_which(toy_find(basename(*argv)), argv); 173 } 174 175 // Multiplexer command, first argument is command to run, rest are args to that. 176 // If first argument starts with - output list of command install paths. 177 void toybox_main(void) 178 { 179 static char *toy_paths[] = {"usr/","bin/","sbin/",0}; 180 int i, len = 0; 181 182 // fast path: try to exec immediately. 183 // (Leave toys.which null to disable suid return logic.) 184 // Try dereferencing one layer of symlink 185 if (toys.argv[1]) { 186 toy_exec(toys.argv+1); 187 if (0<readlink(toys.argv[1], libbuf, sizeof(libbuf))) { 188 struct toy_list *tl= toy_find(basename(libbuf)); 189 190 if (tl == toy_list) unknown(basename(toys.argv[1])); 191 else toy_exec_which(tl, toys.argv+1); 192 } 193 } 194 195 // For early error reporting 196 toys.which = toy_list; 197 198 if (toys.argv[1] && toys.argv[1][0] != '-') unknown(toys.argv[1]); 199 200 // Output list of command. 201 for (i = 1; i<ARRAY_LEN(toy_list); i++) { 202 int fl = toy_list[i].flags; 203 if (fl & TOYMASK_LOCATION) { 204 if (toys.argv[1]) { 205 int j; 206 for (j = 0; toy_paths[j]; j++) 207 if (fl & (1<<j)) len += printf("%s", toy_paths[j]); 208 } 209 len += printf("%s",toy_list[i].name); 210 if (++len > 65) len = 0; 211 xputc(len ? ' ' : '\n'); 212 } 213 } 214 xputc('\n'); 215 } 216 217 int main(int argc, char *argv[]) 218 { 219 // don't segfault if our environment is crazy 220 if (!*argv) return 127; 221 222 // Snapshot stack location so we can detect recursion depth later. 223 // Nommu has special reentry path, !stacktop = "vfork/exec self happened" 224 if (!CFG_TOYBOX_FORK && (0x80 & **argv)) **argv &= 0x7f; 225 else { 226 int stack_start; // here so probe var won't permanently eat stack 227 228 toys.stacktop = &stack_start; 229 } 230 231 // Android before O had non-default SIGPIPE, 7 years = remove in Sep 2024. 232 if (CFG_TOYBOX_ON_ANDROID) signal(SIGPIPE, SIG_DFL); 233 234 if (CFG_TOYBOX) { 235 // Call the multiplexer with argv[] as its arguments so it can toy_find() 236 toys.argv = argv-1; 237 toybox_main(); 238 } else { 239 // single command built standalone with no multiplexer is first list entry 240 toy_singleinit(toy_list, argv); 241 toy_list->toy_main(); 242 } 243 244 xexit(); 245 } 246