21#endif 22 23/* vix 26jan87 [RCS'd; rest of log is in RCS file] 24 * vix 01jan87 [added line-level error recovery] 25 * vix 31dec86 [added /step to the from-to range, per bob@acornrc] 26 * vix 30dec86 [written] 27 */ 28 29 30#include "cron.h" 31#include <grp.h> 32#ifdef LOGIN_CAP 33#include <login_cap.h> 34#endif 35 36typedef enum ecode { 37 e_none, e_minute, e_hour, e_dom, e_month, e_dow, 38 e_cmd, e_timespec, e_username, e_group, e_mem 39#ifdef LOGIN_CAP 40 , e_class 41#endif 42} ecode_e; 43 44static char get_list __P((bitstr_t *, int, int, char *[], int, FILE *)), 45 get_range __P((bitstr_t *, int, int, char *[], int, FILE *)), 46 get_number __P((int *, int, char *[], int, FILE *)); 47static int set_element __P((bitstr_t *, int, int, int)); 48 49static char *ecodes[] = 50 { 51 "no error", 52 "bad minute", 53 "bad hour", 54 "bad day-of-month", 55 "bad month", 56 "bad day-of-week", 57 "bad command", 58 "bad time specifier", 59 "bad username", 60 "bad group name", 61 "out of memory", 62#ifdef LOGIN_CAP 63 "bad class name", 64#endif 65 }; 66 67 68void 69free_entry(e) 70 entry *e; 71{ 72#ifdef LOGIN_CAP 73 if (e->class != NULL) 74 free(e->class); 75#endif 76 if (e->cmd != NULL) 77 free(e->cmd); 78 if (e->envp != NULL) 79 env_free(e->envp); 80 free(e); 81} 82 83 84/* return NULL if eof or syntax error occurs; 85 * otherwise return a pointer to a new entry. 86 */ 87entry * 88load_entry(file, error_func, pw, envp) 89 FILE *file; 90 void (*error_func)(); 91 struct passwd *pw; 92 char **envp; 93{ 94 /* this function reads one crontab entry -- the next -- from a file. 95 * it skips any leading blank lines, ignores comments, and returns 96 * EOF if for any reason the entry can't be read and parsed. 97 * 98 * the entry is also parsed here. 99 * 100 * syntax: 101 * user crontab: 102 * minutes hours doms months dows cmd\n 103 * system crontab (/etc/crontab): 104 * minutes hours doms months dows USERNAME cmd\n 105 */ 106 107 ecode_e ecode = e_none; 108 entry *e; 109 int ch; 110 char cmd[MAX_COMMAND]; 111 char envstr[MAX_ENVSTR]; 112 char **prev_env; 113 114 Debug(DPARS, ("load_entry()...about to eat comments\n")) 115 116 skip_comments(file); 117 118 ch = get_char(file); 119 if (ch == EOF) 120 return NULL; 121 122 /* ch is now the first useful character of a useful line. 123 * it may be an @special or it may be the first character 124 * of a list of minutes. 125 */ 126 127 e = (entry *) calloc(sizeof(entry), sizeof(char)); 128 129 if (e == NULL) { 130 warn("load_entry: calloc failed"); 131 return NULL; 132 } 133 134 if (ch == '@') { 135 /* all of these should be flagged and load-limited; i.e., 136 * instead of @hourly meaning "0 * * * *" it should mean 137 * "close to the front of every hour but not 'til the 138 * system load is low". Problems are: how do you know 139 * what "low" means? (save me from /etc/cron.conf!) and: 140 * how to guarantee low variance (how low is low?), which 141 * means how to we run roughly every hour -- seems like 142 * we need to keep a history or let the first hour set 143 * the schedule, which means we aren't load-limited 144 * anymore. too much for my overloaded brain. (vix, jan90) 145 * HINT 146 */ 147 Debug(DPARS, ("load_entry()...about to test shortcuts\n")) 148 ch = get_string(cmd, MAX_COMMAND, file, " \t\n"); 149 if (!strcmp("reboot", cmd)) { 150 Debug(DPARS, ("load_entry()...reboot shortcut\n")) 151 e->flags |= WHEN_REBOOT; 152 } else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)){ 153 Debug(DPARS, ("load_entry()...yearly shortcut\n")) 154 bit_set(e->minute, 0); 155 bit_set(e->hour, 0); 156 bit_set(e->dom, 0); 157 bit_set(e->month, 0); 158 bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); 159 e->flags |= DOW_STAR; 160 } else if (!strcmp("monthly", cmd)) { 161 Debug(DPARS, ("load_entry()...monthly shortcut\n")) 162 bit_set(e->minute, 0); 163 bit_set(e->hour, 0); 164 bit_set(e->dom, 0); 165 bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); 166 bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); 167 e->flags |= DOW_STAR; 168 } else if (!strcmp("weekly", cmd)) { 169 Debug(DPARS, ("load_entry()...weekly shortcut\n")) 170 bit_set(e->minute, 0); 171 bit_set(e->hour, 0); 172 bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); 173 e->flags |= DOM_STAR; 174 bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); 175 bit_set(e->dow, 0); 176 } else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) { 177 Debug(DPARS, ("load_entry()...daily shortcut\n")) 178 bit_set(e->minute, 0); 179 bit_set(e->hour, 0); 180 bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); 181 bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); 182 bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); 183 } else if (!strcmp("hourly", cmd)) { 184 Debug(DPARS, ("load_entry()...hourly shortcut\n")) 185 bit_set(e->minute, 0); 186 bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1)); 187 bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); 188 bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); 189 bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); 190 } else { 191 ecode = e_timespec; 192 goto eof; 193 } 194 /* Advance past whitespace between shortcut and 195 * username/command. 196 */ 197 Skip_Blanks(ch, file); 198 if (ch == EOF) { 199 ecode = e_cmd; 200 goto eof; 201 } 202 } else { 203 Debug(DPARS, ("load_entry()...about to parse numerics\n")) 204 205 ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE, 206 PPC_NULL, ch, file); 207 if (ch == EOF) { 208 ecode = e_minute; 209 goto eof; 210 } 211 212 /* hours 213 */ 214 215 ch = get_list(e->hour, FIRST_HOUR, LAST_HOUR, 216 PPC_NULL, ch, file); 217 if (ch == EOF) { 218 ecode = e_hour; 219 goto eof; 220 } 221 222 /* DOM (days of month) 223 */ 224 225 if (ch == '*') 226 e->flags |= DOM_STAR; 227 ch = get_list(e->dom, FIRST_DOM, LAST_DOM, 228 PPC_NULL, ch, file); 229 if (ch == EOF) { 230 ecode = e_dom; 231 goto eof; 232 } 233 234 /* month 235 */ 236 237 ch = get_list(e->month, FIRST_MONTH, LAST_MONTH, 238 MonthNames, ch, file); 239 if (ch == EOF) { 240 ecode = e_month; 241 goto eof; 242 } 243 244 /* DOW (days of week) 245 */ 246 247 if (ch == '*') 248 e->flags |= DOW_STAR; 249 ch = get_list(e->dow, FIRST_DOW, LAST_DOW, 250 DowNames, ch, file); 251 if (ch == EOF) { 252 ecode = e_dow; 253 goto eof; 254 } 255 } 256 257 /* make sundays equivilent */ 258 if (bit_test(e->dow, 0) || bit_test(e->dow, 7)) { 259 bit_set(e->dow, 0); 260 bit_set(e->dow, 7); 261 } 262 263 /* ch is the first character of a command, or a username */ 264 unget_char(ch, file); 265 266 if (!pw) { 267 char *username = cmd; /* temp buffer */ 268 char *s; 269 struct group *grp; 270#ifdef LOGIN_CAP 271 login_cap_t *lc; 272#endif 273 274 Debug(DPARS, ("load_entry()...about to parse username\n")) 275 ch = get_string(username, MAX_COMMAND, file, " \t"); 276 277 Debug(DPARS, ("load_entry()...got %s\n",username)) 278 if (ch == EOF) { 279 ecode = e_cmd; 280 goto eof; 281 } 282 283#ifdef LOGIN_CAP 284 if ((s = strrchr(username, '/')) != NULL) { 285 *s = '\0'; 286 e->class = strdup(s + 1); 287 if (e->class == NULL) 288 warn("strdup(\"%s\")", s + 1); 289 } else { 290 e->class = strdup(RESOURCE_RC); 291 if (e->class == NULL) 292 warn("strdup(\"%s\")", RESOURCE_RC); 293 } 294 if (e->class == NULL) { 295 ecode = e_mem; 296 goto eof; 297 } 298 if ((lc = login_getclass(e->class)) == NULL) { 299 ecode = e_class; 300 goto eof; 301 } 302 login_close(lc); 303#endif 304 grp = NULL; 305 if ((s = strrchr(username, ':')) != NULL) { 306 *s = '\0'; 307 if ((grp = getgrnam(s + 1)) == NULL) { 308 ecode = e_group; 309 goto eof; 310 } 311 } 312 313 pw = getpwnam(username); 314 if (pw == NULL) { 315 ecode = e_username; 316 goto eof; 317 } 318 if (grp != NULL) 319 pw->pw_gid = grp->gr_gid; 320 Debug(DPARS, ("load_entry()...uid %d, gid %d\n",pw->pw_uid,pw->pw_gid)) 321#ifdef LOGIN_CAP 322 Debug(DPARS, ("load_entry()...class %s\n",e->class)) 323#endif 324 } 325
| 21#endif 22 23/* vix 26jan87 [RCS'd; rest of log is in RCS file] 24 * vix 01jan87 [added line-level error recovery] 25 * vix 31dec86 [added /step to the from-to range, per bob@acornrc] 26 * vix 30dec86 [written] 27 */ 28 29 30#include "cron.h" 31#include <grp.h> 32#ifdef LOGIN_CAP 33#include <login_cap.h> 34#endif 35 36typedef enum ecode { 37 e_none, e_minute, e_hour, e_dom, e_month, e_dow, 38 e_cmd, e_timespec, e_username, e_group, e_mem 39#ifdef LOGIN_CAP 40 , e_class 41#endif 42} ecode_e; 43 44static char get_list __P((bitstr_t *, int, int, char *[], int, FILE *)), 45 get_range __P((bitstr_t *, int, int, char *[], int, FILE *)), 46 get_number __P((int *, int, char *[], int, FILE *)); 47static int set_element __P((bitstr_t *, int, int, int)); 48 49static char *ecodes[] = 50 { 51 "no error", 52 "bad minute", 53 "bad hour", 54 "bad day-of-month", 55 "bad month", 56 "bad day-of-week", 57 "bad command", 58 "bad time specifier", 59 "bad username", 60 "bad group name", 61 "out of memory", 62#ifdef LOGIN_CAP 63 "bad class name", 64#endif 65 }; 66 67 68void 69free_entry(e) 70 entry *e; 71{ 72#ifdef LOGIN_CAP 73 if (e->class != NULL) 74 free(e->class); 75#endif 76 if (e->cmd != NULL) 77 free(e->cmd); 78 if (e->envp != NULL) 79 env_free(e->envp); 80 free(e); 81} 82 83 84/* return NULL if eof or syntax error occurs; 85 * otherwise return a pointer to a new entry. 86 */ 87entry * 88load_entry(file, error_func, pw, envp) 89 FILE *file; 90 void (*error_func)(); 91 struct passwd *pw; 92 char **envp; 93{ 94 /* this function reads one crontab entry -- the next -- from a file. 95 * it skips any leading blank lines, ignores comments, and returns 96 * EOF if for any reason the entry can't be read and parsed. 97 * 98 * the entry is also parsed here. 99 * 100 * syntax: 101 * user crontab: 102 * minutes hours doms months dows cmd\n 103 * system crontab (/etc/crontab): 104 * minutes hours doms months dows USERNAME cmd\n 105 */ 106 107 ecode_e ecode = e_none; 108 entry *e; 109 int ch; 110 char cmd[MAX_COMMAND]; 111 char envstr[MAX_ENVSTR]; 112 char **prev_env; 113 114 Debug(DPARS, ("load_entry()...about to eat comments\n")) 115 116 skip_comments(file); 117 118 ch = get_char(file); 119 if (ch == EOF) 120 return NULL; 121 122 /* ch is now the first useful character of a useful line. 123 * it may be an @special or it may be the first character 124 * of a list of minutes. 125 */ 126 127 e = (entry *) calloc(sizeof(entry), sizeof(char)); 128 129 if (e == NULL) { 130 warn("load_entry: calloc failed"); 131 return NULL; 132 } 133 134 if (ch == '@') { 135 /* all of these should be flagged and load-limited; i.e., 136 * instead of @hourly meaning "0 * * * *" it should mean 137 * "close to the front of every hour but not 'til the 138 * system load is low". Problems are: how do you know 139 * what "low" means? (save me from /etc/cron.conf!) and: 140 * how to guarantee low variance (how low is low?), which 141 * means how to we run roughly every hour -- seems like 142 * we need to keep a history or let the first hour set 143 * the schedule, which means we aren't load-limited 144 * anymore. too much for my overloaded brain. (vix, jan90) 145 * HINT 146 */ 147 Debug(DPARS, ("load_entry()...about to test shortcuts\n")) 148 ch = get_string(cmd, MAX_COMMAND, file, " \t\n"); 149 if (!strcmp("reboot", cmd)) { 150 Debug(DPARS, ("load_entry()...reboot shortcut\n")) 151 e->flags |= WHEN_REBOOT; 152 } else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)){ 153 Debug(DPARS, ("load_entry()...yearly shortcut\n")) 154 bit_set(e->minute, 0); 155 bit_set(e->hour, 0); 156 bit_set(e->dom, 0); 157 bit_set(e->month, 0); 158 bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); 159 e->flags |= DOW_STAR; 160 } else if (!strcmp("monthly", cmd)) { 161 Debug(DPARS, ("load_entry()...monthly shortcut\n")) 162 bit_set(e->minute, 0); 163 bit_set(e->hour, 0); 164 bit_set(e->dom, 0); 165 bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); 166 bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); 167 e->flags |= DOW_STAR; 168 } else if (!strcmp("weekly", cmd)) { 169 Debug(DPARS, ("load_entry()...weekly shortcut\n")) 170 bit_set(e->minute, 0); 171 bit_set(e->hour, 0); 172 bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); 173 e->flags |= DOM_STAR; 174 bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); 175 bit_set(e->dow, 0); 176 } else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) { 177 Debug(DPARS, ("load_entry()...daily shortcut\n")) 178 bit_set(e->minute, 0); 179 bit_set(e->hour, 0); 180 bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); 181 bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); 182 bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); 183 } else if (!strcmp("hourly", cmd)) { 184 Debug(DPARS, ("load_entry()...hourly shortcut\n")) 185 bit_set(e->minute, 0); 186 bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1)); 187 bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); 188 bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); 189 bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); 190 } else { 191 ecode = e_timespec; 192 goto eof; 193 } 194 /* Advance past whitespace between shortcut and 195 * username/command. 196 */ 197 Skip_Blanks(ch, file); 198 if (ch == EOF) { 199 ecode = e_cmd; 200 goto eof; 201 } 202 } else { 203 Debug(DPARS, ("load_entry()...about to parse numerics\n")) 204 205 ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE, 206 PPC_NULL, ch, file); 207 if (ch == EOF) { 208 ecode = e_minute; 209 goto eof; 210 } 211 212 /* hours 213 */ 214 215 ch = get_list(e->hour, FIRST_HOUR, LAST_HOUR, 216 PPC_NULL, ch, file); 217 if (ch == EOF) { 218 ecode = e_hour; 219 goto eof; 220 } 221 222 /* DOM (days of month) 223 */ 224 225 if (ch == '*') 226 e->flags |= DOM_STAR; 227 ch = get_list(e->dom, FIRST_DOM, LAST_DOM, 228 PPC_NULL, ch, file); 229 if (ch == EOF) { 230 ecode = e_dom; 231 goto eof; 232 } 233 234 /* month 235 */ 236 237 ch = get_list(e->month, FIRST_MONTH, LAST_MONTH, 238 MonthNames, ch, file); 239 if (ch == EOF) { 240 ecode = e_month; 241 goto eof; 242 } 243 244 /* DOW (days of week) 245 */ 246 247 if (ch == '*') 248 e->flags |= DOW_STAR; 249 ch = get_list(e->dow, FIRST_DOW, LAST_DOW, 250 DowNames, ch, file); 251 if (ch == EOF) { 252 ecode = e_dow; 253 goto eof; 254 } 255 } 256 257 /* make sundays equivilent */ 258 if (bit_test(e->dow, 0) || bit_test(e->dow, 7)) { 259 bit_set(e->dow, 0); 260 bit_set(e->dow, 7); 261 } 262 263 /* ch is the first character of a command, or a username */ 264 unget_char(ch, file); 265 266 if (!pw) { 267 char *username = cmd; /* temp buffer */ 268 char *s; 269 struct group *grp; 270#ifdef LOGIN_CAP 271 login_cap_t *lc; 272#endif 273 274 Debug(DPARS, ("load_entry()...about to parse username\n")) 275 ch = get_string(username, MAX_COMMAND, file, " \t"); 276 277 Debug(DPARS, ("load_entry()...got %s\n",username)) 278 if (ch == EOF) { 279 ecode = e_cmd; 280 goto eof; 281 } 282 283#ifdef LOGIN_CAP 284 if ((s = strrchr(username, '/')) != NULL) { 285 *s = '\0'; 286 e->class = strdup(s + 1); 287 if (e->class == NULL) 288 warn("strdup(\"%s\")", s + 1); 289 } else { 290 e->class = strdup(RESOURCE_RC); 291 if (e->class == NULL) 292 warn("strdup(\"%s\")", RESOURCE_RC); 293 } 294 if (e->class == NULL) { 295 ecode = e_mem; 296 goto eof; 297 } 298 if ((lc = login_getclass(e->class)) == NULL) { 299 ecode = e_class; 300 goto eof; 301 } 302 login_close(lc); 303#endif 304 grp = NULL; 305 if ((s = strrchr(username, ':')) != NULL) { 306 *s = '\0'; 307 if ((grp = getgrnam(s + 1)) == NULL) { 308 ecode = e_group; 309 goto eof; 310 } 311 } 312 313 pw = getpwnam(username); 314 if (pw == NULL) { 315 ecode = e_username; 316 goto eof; 317 } 318 if (grp != NULL) 319 pw->pw_gid = grp->gr_gid; 320 Debug(DPARS, ("load_entry()...uid %d, gid %d\n",pw->pw_uid,pw->pw_gid)) 321#ifdef LOGIN_CAP 322 Debug(DPARS, ("load_entry()...class %s\n",e->class)) 323#endif 324 } 325
|
330 331 e->uid = pw->pw_uid; 332 e->gid = pw->pw_gid; 333 334 /* copy and fix up environment. some variables are just defaults and 335 * others are overrides. 336 */ 337 e->envp = env_copy(envp); 338 if (e->envp == NULL) { 339 warn("env_copy"); 340 ecode = e_mem; 341 goto eof; 342 } 343 if (!env_get("SHELL", e->envp)) { 344 prev_env = e->envp; 345 sprintf(envstr, "SHELL=%s", _PATH_BSHELL); 346 e->envp = env_set(e->envp, envstr); 347 if (e->envp == NULL) { 348 warn("env_set(%s)", envstr); 349 env_free(prev_env); 350 ecode = e_mem; 351 goto eof; 352 } 353 } 354 if (!env_get("HOME", e->envp)) { 355 prev_env = e->envp; 356 sprintf(envstr, "HOME=%s", pw->pw_dir); 357 e->envp = env_set(e->envp, envstr); 358 if (e->envp == NULL) { 359 warn("env_set(%s)", envstr); 360 env_free(prev_env); 361 ecode = e_mem; 362 goto eof; 363 } 364 } 365 if (!env_get("PATH", e->envp)) { 366 prev_env = e->envp; 367 sprintf(envstr, "PATH=%s", _PATH_DEFPATH); 368 e->envp = env_set(e->envp, envstr); 369 if (e->envp == NULL) { 370 warn("env_set(%s)", envstr); 371 env_free(prev_env); 372 ecode = e_mem; 373 goto eof; 374 } 375 } 376 prev_env = e->envp; 377 sprintf(envstr, "%s=%s", "LOGNAME", pw->pw_name); 378 e->envp = env_set(e->envp, envstr); 379 if (e->envp == NULL) { 380 warn("env_set(%s)", envstr); 381 env_free(prev_env); 382 ecode = e_mem; 383 goto eof; 384 } 385#if defined(BSD) 386 prev_env = e->envp; 387 sprintf(envstr, "%s=%s", "USER", pw->pw_name); 388 e->envp = env_set(e->envp, envstr); 389 if (e->envp == NULL) { 390 warn("env_set(%s)", envstr); 391 env_free(prev_env); 392 ecode = e_mem; 393 goto eof; 394 } 395#endif 396 397 Debug(DPARS, ("load_entry()...about to parse command\n")) 398 399 /* Everything up to the next \n or EOF is part of the command... 400 * too bad we don't know in advance how long it will be, since we 401 * need to malloc a string for it... so, we limit it to MAX_COMMAND. 402 * XXX - should use realloc(). 403 */ 404 ch = get_string(cmd, MAX_COMMAND, file, "\n"); 405 406 /* a file without a \n before the EOF is rude, so we'll complain... 407 */ 408 if (ch == EOF) { 409 ecode = e_cmd; 410 goto eof; 411 } 412 413 /* got the command in the 'cmd' string; save it in *e. 414 */ 415 e->cmd = strdup(cmd); 416 if (e->cmd == NULL) { 417 warn("strdup(\"%s\")", cmd); 418 ecode = e_mem; 419 goto eof; 420 } 421 Debug(DPARS, ("load_entry()...returning successfully\n")) 422 423 /* success, fini, return pointer to the entry we just created... 424 */ 425 return e; 426 427 eof: 428 free_entry(e); 429 if (ecode != e_none && error_func) 430 (*error_func)(ecodes[(int)ecode]); 431 while (ch != EOF && ch != '\n') 432 ch = get_char(file); 433 return NULL; 434} 435 436 437static char 438get_list(bits, low, high, names, ch, file) 439 bitstr_t *bits; /* one bit per flag, default=FALSE */ 440 int low, high; /* bounds, impl. offset for bitstr */ 441 char *names[]; /* NULL or *[] of names for these elements */ 442 int ch; /* current character being processed */ 443 FILE *file; /* file being read */ 444{ 445 register int done; 446 447 /* we know that we point to a non-blank character here; 448 * must do a Skip_Blanks before we exit, so that the 449 * next call (or the code that picks up the cmd) can 450 * assume the same thing. 451 */ 452 453 Debug(DPARS|DEXT, ("get_list()...entered\n")) 454 455 /* list = range {"," range} 456 */ 457 458 /* clear the bit string, since the default is 'off'. 459 */ 460 bit_nclear(bits, 0, (high-low+1)); 461 462 /* process all ranges 463 */ 464 done = FALSE; 465 while (!done) { 466 ch = get_range(bits, low, high, names, ch, file); 467 if (ch == ',') 468 ch = get_char(file); 469 else 470 done = TRUE; 471 } 472 473 /* exiting. skip to some blanks, then skip over the blanks. 474 */ 475 Skip_Nonblanks(ch, file) 476 Skip_Blanks(ch, file) 477 478 Debug(DPARS|DEXT, ("get_list()...exiting w/ %02x\n", ch)) 479 480 return ch; 481} 482 483 484static char 485get_range(bits, low, high, names, ch, file) 486 bitstr_t *bits; /* one bit per flag, default=FALSE */ 487 int low, high; /* bounds, impl. offset for bitstr */ 488 char *names[]; /* NULL or names of elements */ 489 int ch; /* current character being processed */ 490 FILE *file; /* file being read */ 491{ 492 /* range = number | number "-" number [ "/" number ] 493 */ 494 495 register int i; 496 auto int num1, num2, num3; 497 498 Debug(DPARS|DEXT, ("get_range()...entering, exit won't show\n")) 499 500 if (ch == '*') { 501 /* '*' means "first-last" but can still be modified by /step 502 */ 503 num1 = low; 504 num2 = high; 505 ch = get_char(file); 506 if (ch == EOF) 507 return EOF; 508 } else { 509 if (EOF == (ch = get_number(&num1, low, names, ch, file))) 510 return EOF; 511 512 if (ch == '/') 513 num2 = high; 514 else if (ch != '-') { 515 /* not a range, it's a single number. 516 */ 517 if (EOF == set_element(bits, low, high, num1)) 518 return EOF; 519 return ch; 520 } else { 521 /* eat the dash 522 */ 523 ch = get_char(file); 524 if (ch == EOF) 525 return EOF; 526 527 /* get the number following the dash 528 */ 529 ch = get_number(&num2, low, names, ch, file); 530 if (ch == EOF) 531 return EOF; 532 } 533 } 534 535 /* check for step size 536 */ 537 if (ch == '/') { 538 /* eat the slash 539 */ 540 ch = get_char(file); 541 if (ch == EOF) 542 return EOF; 543 544 /* get the step size -- note: we don't pass the 545 * names here, because the number is not an 546 * element id, it's a step size. 'low' is 547 * sent as a 0 since there is no offset either. 548 */ 549 ch = get_number(&num3, 0, PPC_NULL, ch, file); 550 if (ch == EOF || num3 == 0) 551 return EOF; 552 } else { 553 /* no step. default==1. 554 */ 555 num3 = 1; 556 } 557 558 /* range. set all elements from num1 to num2, stepping 559 * by num3. (the step is a downward-compatible extension 560 * proposed conceptually by bob@acornrc, syntactically 561 * designed then implmented by paul vixie). 562 */ 563 for (i = num1; i <= num2; i += num3) 564 if (EOF == set_element(bits, low, high, i)) 565 return EOF; 566 567 return ch; 568} 569 570 571static char 572get_number(numptr, low, names, ch, file) 573 int *numptr; /* where does the result go? */ 574 int low; /* offset applied to result if symbolic enum used */ 575 char *names[]; /* symbolic names, if any, for enums */ 576 int ch; /* current character */ 577 FILE *file; /* source */ 578{ 579 char temp[MAX_TEMPSTR], *pc; 580 int len, i, all_digits; 581 582 /* collect alphanumerics into our fixed-size temp array 583 */ 584 pc = temp; 585 len = 0; 586 all_digits = TRUE; 587 while (isalnum(ch)) { 588 if (++len >= MAX_TEMPSTR) 589 return EOF; 590 591 *pc++ = ch; 592 593 if (!isdigit(ch)) 594 all_digits = FALSE; 595 596 ch = get_char(file); 597 } 598 *pc = '\0'; 599 if (len == 0) 600 return (EOF); 601 602 /* try to find the name in the name list 603 */ 604 if (names) { 605 for (i = 0; names[i] != NULL; i++) { 606 Debug(DPARS|DEXT, 607 ("get_num, compare(%s,%s)\n", names[i], temp)) 608 if (!strcasecmp(names[i], temp)) { 609 *numptr = i+low; 610 return ch; 611 } 612 } 613 } 614 615 /* no name list specified, or there is one and our string isn't 616 * in it. either way: if it's all digits, use its magnitude. 617 * otherwise, it's an error. 618 */ 619 if (all_digits) { 620 *numptr = atoi(temp); 621 return ch; 622 } 623 624 return EOF; 625} 626 627 628static int 629set_element(bits, low, high, number) 630 bitstr_t *bits; /* one bit per flag, default=FALSE */ 631 int low; 632 int high; 633 int number; 634{ 635 Debug(DPARS|DEXT, ("set_element(?,%d,%d,%d)\n", low, high, number)) 636 637 if (number < low || number > high) 638 return EOF; 639 640 bit_set(bits, (number-low)); 641 return OK; 642}
| 332 333 e->uid = pw->pw_uid; 334 e->gid = pw->pw_gid; 335 336 /* copy and fix up environment. some variables are just defaults and 337 * others are overrides. 338 */ 339 e->envp = env_copy(envp); 340 if (e->envp == NULL) { 341 warn("env_copy"); 342 ecode = e_mem; 343 goto eof; 344 } 345 if (!env_get("SHELL", e->envp)) { 346 prev_env = e->envp; 347 sprintf(envstr, "SHELL=%s", _PATH_BSHELL); 348 e->envp = env_set(e->envp, envstr); 349 if (e->envp == NULL) { 350 warn("env_set(%s)", envstr); 351 env_free(prev_env); 352 ecode = e_mem; 353 goto eof; 354 } 355 } 356 if (!env_get("HOME", e->envp)) { 357 prev_env = e->envp; 358 sprintf(envstr, "HOME=%s", pw->pw_dir); 359 e->envp = env_set(e->envp, envstr); 360 if (e->envp == NULL) { 361 warn("env_set(%s)", envstr); 362 env_free(prev_env); 363 ecode = e_mem; 364 goto eof; 365 } 366 } 367 if (!env_get("PATH", e->envp)) { 368 prev_env = e->envp; 369 sprintf(envstr, "PATH=%s", _PATH_DEFPATH); 370 e->envp = env_set(e->envp, envstr); 371 if (e->envp == NULL) { 372 warn("env_set(%s)", envstr); 373 env_free(prev_env); 374 ecode = e_mem; 375 goto eof; 376 } 377 } 378 prev_env = e->envp; 379 sprintf(envstr, "%s=%s", "LOGNAME", pw->pw_name); 380 e->envp = env_set(e->envp, envstr); 381 if (e->envp == NULL) { 382 warn("env_set(%s)", envstr); 383 env_free(prev_env); 384 ecode = e_mem; 385 goto eof; 386 } 387#if defined(BSD) 388 prev_env = e->envp; 389 sprintf(envstr, "%s=%s", "USER", pw->pw_name); 390 e->envp = env_set(e->envp, envstr); 391 if (e->envp == NULL) { 392 warn("env_set(%s)", envstr); 393 env_free(prev_env); 394 ecode = e_mem; 395 goto eof; 396 } 397#endif 398 399 Debug(DPARS, ("load_entry()...about to parse command\n")) 400 401 /* Everything up to the next \n or EOF is part of the command... 402 * too bad we don't know in advance how long it will be, since we 403 * need to malloc a string for it... so, we limit it to MAX_COMMAND. 404 * XXX - should use realloc(). 405 */ 406 ch = get_string(cmd, MAX_COMMAND, file, "\n"); 407 408 /* a file without a \n before the EOF is rude, so we'll complain... 409 */ 410 if (ch == EOF) { 411 ecode = e_cmd; 412 goto eof; 413 } 414 415 /* got the command in the 'cmd' string; save it in *e. 416 */ 417 e->cmd = strdup(cmd); 418 if (e->cmd == NULL) { 419 warn("strdup(\"%s\")", cmd); 420 ecode = e_mem; 421 goto eof; 422 } 423 Debug(DPARS, ("load_entry()...returning successfully\n")) 424 425 /* success, fini, return pointer to the entry we just created... 426 */ 427 return e; 428 429 eof: 430 free_entry(e); 431 if (ecode != e_none && error_func) 432 (*error_func)(ecodes[(int)ecode]); 433 while (ch != EOF && ch != '\n') 434 ch = get_char(file); 435 return NULL; 436} 437 438 439static char 440get_list(bits, low, high, names, ch, file) 441 bitstr_t *bits; /* one bit per flag, default=FALSE */ 442 int low, high; /* bounds, impl. offset for bitstr */ 443 char *names[]; /* NULL or *[] of names for these elements */ 444 int ch; /* current character being processed */ 445 FILE *file; /* file being read */ 446{ 447 register int done; 448 449 /* we know that we point to a non-blank character here; 450 * must do a Skip_Blanks before we exit, so that the 451 * next call (or the code that picks up the cmd) can 452 * assume the same thing. 453 */ 454 455 Debug(DPARS|DEXT, ("get_list()...entered\n")) 456 457 /* list = range {"," range} 458 */ 459 460 /* clear the bit string, since the default is 'off'. 461 */ 462 bit_nclear(bits, 0, (high-low+1)); 463 464 /* process all ranges 465 */ 466 done = FALSE; 467 while (!done) { 468 ch = get_range(bits, low, high, names, ch, file); 469 if (ch == ',') 470 ch = get_char(file); 471 else 472 done = TRUE; 473 } 474 475 /* exiting. skip to some blanks, then skip over the blanks. 476 */ 477 Skip_Nonblanks(ch, file) 478 Skip_Blanks(ch, file) 479 480 Debug(DPARS|DEXT, ("get_list()...exiting w/ %02x\n", ch)) 481 482 return ch; 483} 484 485 486static char 487get_range(bits, low, high, names, ch, file) 488 bitstr_t *bits; /* one bit per flag, default=FALSE */ 489 int low, high; /* bounds, impl. offset for bitstr */ 490 char *names[]; /* NULL or names of elements */ 491 int ch; /* current character being processed */ 492 FILE *file; /* file being read */ 493{ 494 /* range = number | number "-" number [ "/" number ] 495 */ 496 497 register int i; 498 auto int num1, num2, num3; 499 500 Debug(DPARS|DEXT, ("get_range()...entering, exit won't show\n")) 501 502 if (ch == '*') { 503 /* '*' means "first-last" but can still be modified by /step 504 */ 505 num1 = low; 506 num2 = high; 507 ch = get_char(file); 508 if (ch == EOF) 509 return EOF; 510 } else { 511 if (EOF == (ch = get_number(&num1, low, names, ch, file))) 512 return EOF; 513 514 if (ch == '/') 515 num2 = high; 516 else if (ch != '-') { 517 /* not a range, it's a single number. 518 */ 519 if (EOF == set_element(bits, low, high, num1)) 520 return EOF; 521 return ch; 522 } else { 523 /* eat the dash 524 */ 525 ch = get_char(file); 526 if (ch == EOF) 527 return EOF; 528 529 /* get the number following the dash 530 */ 531 ch = get_number(&num2, low, names, ch, file); 532 if (ch == EOF) 533 return EOF; 534 } 535 } 536 537 /* check for step size 538 */ 539 if (ch == '/') { 540 /* eat the slash 541 */ 542 ch = get_char(file); 543 if (ch == EOF) 544 return EOF; 545 546 /* get the step size -- note: we don't pass the 547 * names here, because the number is not an 548 * element id, it's a step size. 'low' is 549 * sent as a 0 since there is no offset either. 550 */ 551 ch = get_number(&num3, 0, PPC_NULL, ch, file); 552 if (ch == EOF || num3 == 0) 553 return EOF; 554 } else { 555 /* no step. default==1. 556 */ 557 num3 = 1; 558 } 559 560 /* range. set all elements from num1 to num2, stepping 561 * by num3. (the step is a downward-compatible extension 562 * proposed conceptually by bob@acornrc, syntactically 563 * designed then implmented by paul vixie). 564 */ 565 for (i = num1; i <= num2; i += num3) 566 if (EOF == set_element(bits, low, high, i)) 567 return EOF; 568 569 return ch; 570} 571 572 573static char 574get_number(numptr, low, names, ch, file) 575 int *numptr; /* where does the result go? */ 576 int low; /* offset applied to result if symbolic enum used */ 577 char *names[]; /* symbolic names, if any, for enums */ 578 int ch; /* current character */ 579 FILE *file; /* source */ 580{ 581 char temp[MAX_TEMPSTR], *pc; 582 int len, i, all_digits; 583 584 /* collect alphanumerics into our fixed-size temp array 585 */ 586 pc = temp; 587 len = 0; 588 all_digits = TRUE; 589 while (isalnum(ch)) { 590 if (++len >= MAX_TEMPSTR) 591 return EOF; 592 593 *pc++ = ch; 594 595 if (!isdigit(ch)) 596 all_digits = FALSE; 597 598 ch = get_char(file); 599 } 600 *pc = '\0'; 601 if (len == 0) 602 return (EOF); 603 604 /* try to find the name in the name list 605 */ 606 if (names) { 607 for (i = 0; names[i] != NULL; i++) { 608 Debug(DPARS|DEXT, 609 ("get_num, compare(%s,%s)\n", names[i], temp)) 610 if (!strcasecmp(names[i], temp)) { 611 *numptr = i+low; 612 return ch; 613 } 614 } 615 } 616 617 /* no name list specified, or there is one and our string isn't 618 * in it. either way: if it's all digits, use its magnitude. 619 * otherwise, it's an error. 620 */ 621 if (all_digits) { 622 *numptr = atoi(temp); 623 return ch; 624 } 625 626 return EOF; 627} 628 629 630static int 631set_element(bits, low, high, number) 632 bitstr_t *bits; /* one bit per flag, default=FALSE */ 633 int low; 634 int high; 635 int number; 636{ 637 Debug(DPARS|DEXT, ("set_element(?,%d,%d,%d)\n", low, high, number)) 638 639 if (number < low || number > high) 640 return EOF; 641 642 bit_set(bits, (number-low)); 643 return OK; 644}
|