const char* FAST_FUNC strip_unsafe_prefix(const char *str) { const char *cp = str; while (1) { char *cp2; if (*cp == '/') { cp++; continue; } if (is_prefixed_with(cp, "/../"+1)) { cp += 3; continue; } cp2 = strstr(cp, "/../"); if (!cp2) break; cp = cp2 + 4; } if (cp != str) { static smallint warned = 0; if (!warned) { warned = 1; bb_error_msg("removing leading '%.*s' from member names", (int)(cp - str), str); } } return cp; }
static const char *if_redefined(const char *var, const char *key, const char *line) { if (!is_prefixed_with(line, key)) return var; line += strlen(key); if (!isspace(line[0])) return var; return xstrdup(skip_whitespace(line)); }
int FAST_FUNC index_in_substr_array(const char *const string_array[], const char *key) { int i; if (key[0]) { for (i = 0; string_array[i] != 0; i++) { if (is_prefixed_with(string_array[i], key)) { return i; } } } return -1; }
static NOINLINE bool may_wakeup(const char *rtcname) { ssize_t ret; char buf[128]; /* strip "/dev/" from the rtcname here */ rtcname = skip_dev_pfx(rtcname); snprintf(buf, sizeof(buf), SYS_RTC_PATH, rtcname); ret = open_read_close(buf, buf, sizeof(buf)); if (ret < 0) return false; /* wakeup events could be disabled or not supported */ return is_prefixed_with(buf, "enabled\n") != NULL; }
static void sgi_write_table(void) { sgilabel->csum = 0; sgilabel->csum = SGI_SSWAP32(two_s_complement_32bit_sum( (unsigned int*)sgilabel, sizeof(*sgilabel))); assert(two_s_complement_32bit_sum( (unsigned int*)sgilabel, sizeof(*sgilabel)) == 0); write_sector(0, sgilabel); if (is_prefixed_with((char*)sgilabel->directory[0].vol_file_name, "sgilabel")) { /* * keep this habit of first writing the "sgilabel". * I never tested whether it works without (AN 981002). */ sgiinfo *info = fill_sgiinfo(); int infostartblock = SGI_SSWAP32(sgilabel->directory[0].vol_file_start); write_sector(infostartblock, info); free(info); } }
static char* find_keyword(char *ptr, size_t len, const char *word) { if (!ptr) /* happens if xmalloc_open_zipped_read_close cannot read it */ return NULL; len -= strlen(word) - 1; while ((ssize_t)len > 0) { char *old = ptr; char *after_word; /* search for the first char in word */ ptr = memchr(ptr, word[0], len); if (ptr == NULL) /* no occurance left, done */ break; after_word = is_prefixed_with(ptr, word); if (after_word) return after_word; /* found, return ptr past it */ ++ptr; len -= (ptr - old); } return NULL; }
int setarch_main(int argc UNUSED_PARAM, char **argv) { unsigned opts; unsigned long pers; /* Figure out what personality we are supposed to switch to ... * we can be invoked as either: * argv[0],argv[1] == "setarch","personality" * argv[0] == "personality" */ if (ENABLE_SETARCH && applet_name[0] == 's' && argv[1] && is_prefixed_with(argv[1], "linux") ) { applet_name = argv[1]; argv++; } if (applet_name[5] == '6') /* linux64 */ pers = PER_LINUX; else if (applet_name[5] == '3') /* linux32 */ pers = PER_LINUX32; else bb_show_usage(); opts = getopt32(argv, "+R"); /* '+': stop at first non-option */ if (opts) pers |= ADDR_NO_RANDOMIZE; /* Try to set personality */ if (personality(pers) < 0) bb_perror_msg_and_die("personality(0x%lx)", pers); argv += optind; if (!argv[0]) (--argv)[0] = (char*)"/bin/sh"; /* Try to execute the program */ BB_EXECVP_or_die(argv); }
static void add_probe(const char *name) { struct module_entry *m; m = get_or_add_modentry(name); if (!(option_mask32 & (OPT_REMOVE | OPT_SHOW_DEPS)) && (m->flags & (MODULE_FLAG_LOADED | MODULE_FLAG_BUILTIN)) ) { DBG("skipping %s, it is already loaded", name); return; } DBG("queuing %s", name); m->probed_name = name; m->flags |= MODULE_FLAG_NEED_DEPS; llist_add_to_end(&G.probes, m); G.num_unresolved_deps++; if (ENABLE_FEATURE_MODUTILS_SYMBOLS && is_prefixed_with(m->modname, "symbol:") ) { G.need_symbols = 1; } }
char* FAST_FUNC skip_dev_pfx(const char *tty_name) { if (is_prefixed_with(tty_name, "/dev/")) tty_name += 5; return (char*)tty_name; }
static void load_crontab(const char *fileName) { struct parser_t *parser; struct stat sbuf; int maxLines; char *tokens[6]; #if ENABLE_FEATURE_CROND_CALL_SENDMAIL char *mailTo = NULL; #endif char *shell = NULL; delete_cronfile(fileName); if (!getpwnam(fileName)) { log7("ignoring file '%s' (no such user)", fileName); return; } parser = config_open(fileName); if (!parser) return; maxLines = (strcmp(fileName, "root") == 0) ? 65535 : MAXLINES; if (fstat(fileno(parser->fp), &sbuf) == 0 && sbuf.st_uid == DAEMON_UID) { CronFile *file = xzalloc(sizeof(CronFile)); CronLine **pline; int n; file->cf_username = xstrdup(fileName); pline = &file->cf_lines; while (1) { CronLine *line; if (!--maxLines) { bb_error_msg("user %s: too many lines", fileName); break; } n = config_read(parser, tokens, 6, 1, "# \t", PARSE_NORMAL | PARSE_KEEP_COPY); if (!n) break; log5("user:%s entry:%s", fileName, parser->data); /* check if line is setting MAILTO= */ if (is_prefixed_with(tokens[0], "MAILTO=")) { #if ENABLE_FEATURE_CROND_CALL_SENDMAIL free(mailTo); mailTo = (tokens[0][7]) ? xstrdup(&tokens[0][7]) : NULL; #endif /* otherwise just ignore such lines */ continue; } if (is_prefixed_with(tokens[0], "SHELL=")) { free(shell); shell = xstrdup(&tokens[0][6]); continue; } //TODO: handle HOME= too? "man crontab" says: //name = value // //where the spaces around the equal-sign (=) are optional, and any subsequent //non-leading spaces in value will be part of the value assigned to name. //The value string may be placed in quotes (single or double, but matching) //to preserve leading or trailing blanks. // //Several environment variables are set up automatically by the cron(8) daemon. //SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd //line of the crontab's owner. HOME and SHELL may be overridden by settings //in the crontab; LOGNAME may not. /* check if a minimum of tokens is specified */ if (n < 6) continue; *pline = line = xzalloc(sizeof(*line)); /* parse date ranges */ ParseField(file->cf_username, line->cl_Mins, 60, 0, NULL, tokens[0]); ParseField(file->cf_username, line->cl_Hrs, 24, 0, NULL, tokens[1]); ParseField(file->cf_username, line->cl_Days, 32, 0, NULL, tokens[2]); ParseField(file->cf_username, line->cl_Mons, 12, -1, MonAry, tokens[3]); ParseField(file->cf_username, line->cl_Dow, 7, 0, DowAry, tokens[4]); /* * fix days and dow - if one is not "*" and the other * is "*", the other is set to 0, and vise-versa */ FixDayDow(line); #if ENABLE_FEATURE_CROND_CALL_SENDMAIL /* copy mailto (can be NULL) */ line->cl_mailto = xstrdup(mailTo); #endif line->cl_shell = xstrdup(shell); /* copy command */ line->cl_cmd = xstrdup(tokens[5]); pline = &line->cl_next; //bb_error_msg("M[%s]F[%s][%s][%s][%s][%s][%s]", mailTo, tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]); } *pline = NULL; file->cf_next = G.cron_files; G.cron_files = file; } config_close(parser); #if ENABLE_FEATURE_CROND_CALL_SENDMAIL free(mailTo); #endif free(shell); }
static void load_crontab(const char *fileName) { struct parser_t *parser; struct stat sbuf; int maxLines; char *tokens[6]; #if ENABLE_FEATURE_CROND_CALL_SENDMAIL char *mailTo = NULL; #endif char *shell = NULL; delete_cronfile(fileName); if (!getpwnam(fileName)) { log7("ignoring file '%s' (no such user)", fileName); return; } parser = config_open(fileName); if (!parser) return; maxLines = (strcmp(fileName, "root") == 0) ? 65535 : MAXLINES; if (fstat(fileno(parser->fp), &sbuf) == 0 && sbuf.st_uid == DAEMON_UID) { CronFile *file = xzalloc(sizeof(CronFile)); CronLine **pline; int n; file->cf_username = xstrdup(fileName); pline = &file->cf_lines; while (1) { CronLine *line; if (!--maxLines) { bb_error_msg("user %s: too many lines", fileName); break; } n = config_read(parser, tokens, 6, 1, "# \t", PARSE_NORMAL | PARSE_KEEP_COPY); if (!n) break; log5("user:%s entry:%s", fileName, parser->data); /* check if line is setting MAILTO= */ if (is_prefixed_with(tokens[0], "MAILTO=")) { #if ENABLE_FEATURE_CROND_CALL_SENDMAIL free(mailTo); mailTo = (tokens[0][7]) ? xstrdup(&tokens[0][7]) : NULL; #endif /* otherwise just ignore such lines */ continue; } if (is_prefixed_with(tokens[0], "SHELL=")) { free(shell); shell = xstrdup(&tokens[0][6]); continue; } //TODO: handle HOME= too? "man crontab" says: //name = value // //where the spaces around the equal-sign (=) are optional, and any subsequent //non-leading spaces in value will be part of the value assigned to name. //The value string may be placed in quotes (single or double, but matching) //to preserve leading or trailing blanks. // //Several environment variables are set up automatically by the cron(8) daemon. //SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd //line of the crontab's owner. HOME and SHELL may be overridden by settings //in the crontab; LOGNAME may not. #if ENABLE_FEATURE_CROND_SPECIAL_TIMES if (tokens[0][0] == '@') { /* * "@daily /a/script/to/run PARAM1 PARAM2..." */ typedef struct SpecialEntry { const char *name; const char tokens[8]; } SpecialEntry; static const SpecialEntry SpecAry[] = { /* hour day month weekday */ { "yearly", "0\0" "1\0" "1\0" "*" }, { "annually", "0\0" "1\0" "1\0" "*" }, { "monthly", "0\0" "1\0" "*\0" "*" }, { "weekly", "0\0" "*\0" "*\0" "0" }, { "daily", "0\0" "*\0" "*\0" "*" }, { "midnight", "0\0" "*\0" "*\0" "*" }, { "hourly", "*\0" "*\0" "*\0" "*" }, { "reboot", "" }, }; const SpecialEntry *e = SpecAry; if (n < 2) continue; for (;;) { if (strcmp(e->name, tokens[0] + 1) == 0) { /* * tokens[1] is only the first word of command, * can'r use it. * find the entire command in unmodified string: */ tokens[5] = skip_whitespace( skip_non_whitespace( skip_whitespace(parser->data))); if (e->tokens[0]) { char *et = (char*)e->tokens; /* minute is "0" for all specials */ tokens[0] = (char*)"0"; tokens[1] = et; tokens[2] = et + 2; tokens[3] = et + 4; tokens[4] = et + 6; } goto got_it; } if (!e->tokens[0]) break; e++; } continue; /* bad line (unrecognized '@foo') */ } #endif /* check if a minimum of tokens is specified */ if (n < 6) continue; IF_FEATURE_CROND_SPECIAL_TIMES( got_it: ) *pline = line = xzalloc(sizeof(*line)); #if ENABLE_FEATURE_CROND_SPECIAL_TIMES if (tokens[0][0] == '@') { /* "@reboot" line */ file->cf_wants_starting = 1; line->cl_pid = START_ME_REBOOT; /* wants to start */ /* line->cl_Mins/Hrs/etc stay zero: never match any time */ } else #endif { /* parse date ranges */ ParseField(file->cf_username, line->cl_Mins, 60, 0, NULL, tokens[0]); ParseField(file->cf_username, line->cl_Hrs, 24, 0, NULL, tokens[1]); ParseField(file->cf_username, line->cl_Days, 32, 0, NULL, tokens[2]); ParseField(file->cf_username, line->cl_Mons, 12, -1, MonAry, tokens[3]); ParseField(file->cf_username, line->cl_Dow, 7, 0, DowAry, tokens[4]); /* * fix days and dow - if one is not "*" and the other * is "*", the other is set to 0, and vise-versa */ FixDayDow(line); } #if ENABLE_FEATURE_CROND_CALL_SENDMAIL /* copy mailto (can be NULL) */ line->cl_mailto = xstrdup(mailTo); #endif line->cl_shell = xstrdup(shell); /* copy command */ line->cl_cmd = xstrdup(tokens[5]); pline = &line->cl_next; //bb_error_msg("M[%s]F[%s][%s][%s][%s][%s][%s]", mailTo, tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]); } *pline = NULL; file->cf_next = G.cron_files; G.cron_files = file; }
static int xbsd_initlabel(struct partition *p) { struct xbsd_disklabel *d = &xbsd_dlabel; struct xbsd_partition *pp; get_geometry(); memset(d, 0, sizeof(struct xbsd_disklabel)); d->d_magic = BSD_DISKMAGIC; if (is_prefixed_with(disk_device, "/dev/sd")) d->d_type = BSD_DTYPE_SCSI; else d->d_type = BSD_DTYPE_ST506; #if !defined(__alpha__) d->d_flags = BSD_D_DOSPART; #else d->d_flags = 0; #endif d->d_secsize = SECTOR_SIZE; /* bytes/sector */ d->d_nsectors = g_sectors; /* sectors/track */ d->d_ntracks = g_heads; /* tracks/cylinder (heads) */ d->d_ncylinders = g_cylinders; d->d_secpercyl = g_sectors * g_heads;/* sectors/cylinder */ if (d->d_secpercyl == 0) d->d_secpercyl = 1; /* avoid segfaults */ d->d_secperunit = d->d_secpercyl * d->d_ncylinders; d->d_rpm = 3600; d->d_interleave = 1; d->d_trackskew = 0; d->d_cylskew = 0; d->d_headswitch = 0; d->d_trkseek = 0; d->d_magic2 = BSD_DISKMAGIC; d->d_bbsize = BSD_BBSIZE; d->d_sbsize = BSD_SBSIZE; #if !defined(__alpha__) d->d_npartitions = 4; pp = &d->d_partitions[2]; /* Partition C should be NetBSD partition */ pp->p_offset = get_start_sect(p); pp->p_size = get_nr_sects(p); pp->p_fstype = BSD_FS_UNUSED; pp = &d->d_partitions[3]; /* Partition D should be whole disk */ pp->p_offset = 0; pp->p_size = d->d_secperunit; pp->p_fstype = BSD_FS_UNUSED; #else d->d_npartitions = 3; pp = &d->d_partitions[2]; /* Partition C should be the whole disk */ pp->p_offset = 0; pp->p_size = d->d_secperunit; pp->p_fstype = BSD_FS_UNUSED; #endif return 1; }
static void modinfo(const char *path, const char *version, const struct modinfo_env *env) { static const char *const shortcuts[] = { "filename", "license", "author", "description", "version", "alias", "srcversion", "depends", "uts_release", "vermagic", "parm", "firmware", }; size_t len; int j; char *ptr, *fullpath, *the_module; const char *field = env->field; int tags = env->tags; if (tags & 1) { /* filename */ display(path, shortcuts[0], 1 != tags); } len = MAXINT(ssize_t); the_module = xmalloc_open_zipped_read_close(path, &len); if (!the_module) { if (path[0] == '/') return; /* Newer depmod puts relative paths in modules.dep */ fullpath = xasprintf("%s/%s/%s", CONFIG_DEFAULT_MODULES_DIR, version, path); the_module = xmalloc_open_zipped_read_close(fullpath, &len); #ifdef DONT_USE_UTS_REL_FOLDER if (!the_module) { free((char*)fullpath); fullpath = xasprintf("%s/%s", CONFIG_DEFAULT_MODULES_DIR, path); the_module = xmalloc_open_zipped_read_close(fullpath, &len); } #endif free((char*)fullpath); if (!the_module) { // outputs system error msg bb_perror_msg(""); return; } } if (field) tags |= OPT_F; for (j = 1; (1<<j) & (OPT_TAGS + OPT_F); j++) { const char *pattern; if (!((1<<j) & tags)) continue; pattern = field; if ((1<<j) & OPT_TAGS) pattern = shortcuts[j]; ptr = the_module; while (1) { char *after_pattern; ptr = memchr(ptr, *pattern, len - (ptr - (char*)the_module)); if (ptr == NULL) /* no occurance left, done */ break; after_pattern = is_prefixed_with(ptr, pattern); if (after_pattern && *after_pattern == '=') { /* field prefixes are 0x80 or 0x00 */ if ((ptr[-1] & 0x7F) == 0x00) { ptr = after_pattern + 1; display(ptr, pattern, (1<<j) != tags); ptr += strlen(ptr); } } ++ptr; } } free(the_module); }
static int run_pipe(char *man_filename, int man, int level) { char *cmd; /* Prevent man page link loops */ if (level > 10) return 0; if (access(man_filename, R_OK) != 0) return 0; if (option_mask32 & OPT_w) { puts(man_filename); return 1; } if (man) { /* man page, not cat page */ /* Is this a link to another manpage? */ /* The link has the following on the first line: */ /* ".so another_man_page" */ struct stat sb; char *line; char *linkname, *p; /* On my system: * man1/genhostid.1.gz: 203 bytes - smallest real manpage * man2/path_resolution.2.gz: 114 bytes - largest link */ xstat(man_filename, &sb); if (sb.st_size > 300) /* err on the safe side */ goto ordinary_manpage; line = xmalloc_open_zipped_read_close(man_filename, NULL); if (!line || !is_prefixed_with(line, ".so ")) { free(line); goto ordinary_manpage; } /* Example: man2/path_resolution.2.gz contains * ".so man7/path_resolution.7\n<junk>" */ *strchrnul(line, '\n') = '\0'; linkname = skip_whitespace(&line[4]); /* If link has no slashes, we just replace man page name. * If link has slashes (however many), we go back *once*. * ".so zzz/ggg/page.3" does NOT go back two levels. */ p = strrchr(man_filename, '/'); if (!p) goto ordinary_manpage; *p = '\0'; if (strchr(linkname, '/')) { p = strrchr(man_filename, '/'); if (!p) goto ordinary_manpage; *p = '\0'; } /* Links do not have .gz extensions, even if manpage * is compressed */ man_filename = xasprintf("%s/%s", man_filename, linkname); free(line); /* Note: we leak "new" man_filename string as well... */ if (show_manpage(man_filename, man, level + 1)) return 1; /* else: show the link, it's better than nothing */ } ordinary_manpage: close(STDIN_FILENO); open_zipped(man_filename, /*fail_if_not_compressed:*/ 0); /* guaranteed to use fd 0 (STDIN_FILENO) */ if (man) { int w = get_terminal_width(-1); if (w > 10) w -= 2; /* "2>&1" is added so that nroff errors are shown in pager too. * Otherwise it may show just empty screen. */ cmd = xasprintf("%s | %s -rLL=%un -rLT=%un 2>&1 | %s", G.tbl, G.nroff, w, w, G.pager); } else { cmd = xstrdup(G.pager); } system(cmd); free(cmd); return 1; }
int patch_main(int argc UNUSED_PARAM, char **argv) { int opts; int reverse, state = 0; char *oldname = NULL, *newname = NULL; char *opt_p, *opt_i; long oldlen = oldlen; /* for compiler */ long newlen = newlen; /* for compiler */ INIT_TT(); opts = getopt32(argv, FLAG_STR, &opt_p, &opt_i); argv += optind; reverse = opts & FLAG_REVERSE; TT.prefix = (opts & FLAG_PATHLEN) ? xatoi(opt_p) : 0; // can be negative! TT.filein = TT.fileout = -1; if (opts & FLAG_INPUT) { xmove_fd(xopen_stdin(opt_i), STDIN_FILENO); } else { if (argv[0] && argv[1]) { xmove_fd(xopen_stdin(argv[1]), STDIN_FILENO); } } // Loop through the lines in the patch for(;;) { char *patchline; patchline = xmalloc_fgetline(stdin); if (!patchline) break; // Other versions of patch accept damaged patches, // so we need to also. if (!*patchline) { free(patchline); patchline = xstrdup(" "); } // Are we assembling a hunk? if (state >= 2) { if (*patchline==' ' || *patchline=='+' || *patchline=='-') { dlist_add(&TT.current_hunk, patchline); if (*patchline != '+') oldlen--; if (*patchline != '-') newlen--; // Context line? if (*patchline==' ' && state==2) TT.context++; else state=3; // If we've consumed all expected hunk lines, apply the hunk. if (!oldlen && !newlen) state = apply_one_hunk(); continue; } fail_hunk(); state = 0; continue; } // Open a new file? if (is_prefixed_with(patchline, "--- ") || is_prefixed_with(patchline, "+++ ")) { char *s, **name = reverse ? &newname : &oldname; int i; if (*patchline == '+') { name = reverse ? &oldname : &newname; state = 1; } finish_oldfile(); if (!argv[0]) { free(*name); // Trim date from end of filename (if any). We don't care. for (s = patchline+4; *s && *s!='\t'; s++) if (*s=='\\' && s[1]) s++; i = atoi(s); if (i>1900 && i<=1970) *name = xstrdup("/dev/null"); else { *s = 0; *name = xstrdup(patchline+4); } } // We defer actually opening the file because svn produces broken // patches that don't signal they want to create a new file the // way the patch man page says, so you have to read the first hunk // and _guess_. // Start a new hunk? Usually @@ -oldline,oldlen +newline,newlen @@ // but a missing ,value means the value is 1. } else if (state == 1 && is_prefixed_with(patchline, "@@ -")) { int i; char *s = patchline+4; // Read oldline[,oldlen] +newline[,newlen] TT.oldlen = oldlen = TT.newlen = newlen = 1; TT.oldline = strtol(s, &s, 10); if (*s == ',') TT.oldlen = oldlen = strtol(s+1, &s, 10); TT.newline = strtol(s+2, &s, 10); if (*s == ',') TT.newlen = newlen = strtol(s+1, &s, 10); if (oldlen < 1 && newlen < 1) bb_error_msg_and_die("Really? %s", patchline); TT.context = 0; state = 2; // If the --- line is missing or malformed, either oldname // or (for -R) newname could be NULL -- but not both. Like // GNU patch, proceed based on the +++ line, and avoid SEGVs. if (!oldname) oldname = xstrdup("MISSING_FILENAME"); if (!newname) newname = xstrdup("MISSING_FILENAME"); // If this is the first hunk, open the file. if (TT.filein == -1) { int oldsum, newsum, empty = 0; char *name; oldsum = TT.oldline + oldlen; newsum = TT.newline + newlen; name = reverse ? oldname : newname; // We're deleting oldname if new file is /dev/null (before -p) // or if new hunk is empty (zero context) after patching if (!strcmp(name, "/dev/null") || !(reverse ? oldsum : newsum)) { name = reverse ? newname : oldname; empty = 1; } // Handle -p path truncation. for (i = 0, s = name; *s;) { if ((option_mask32 & FLAG_PATHLEN) && TT.prefix == i) break; if (*s++ != '/') continue; while (*s == '/') s++; i++; name = s; } // If "patch FILE_TO_PATCH", completely ignore name from patch if (argv[0]) name = argv[0]; if (empty) { // File is empty after the patches have been applied state = 0; if (option_mask32 & FLAG_RMEMPTY) { // If flag -E or --remove-empty-files is set printf("removing %s\n", name); xunlink(name); } else { printf("patching file %s\n", name); xclose(xopen(name, O_WRONLY | O_TRUNC)); } // If we've got a file to open, do so. } else if (!(option_mask32 & FLAG_PATHLEN) || i <= TT.prefix) { struct stat statbuf; // If the old file was null, we're creating a new one. if (!strcmp(oldname, "/dev/null") || !oldsum) { printf("creating %s\n", name); s = strrchr(name, '/'); if (s) { *s = 0; bb_make_directory(name, -1, FILEUTILS_RECUR); *s = '/'; } TT.filein = xopen(name, O_CREAT|O_EXCL|O_RDWR); } else { printf("patching file %s\n", name); TT.filein = xopen(name, O_RDONLY); } TT.tempname = xasprintf("%sXXXXXX", name); TT.fileout = xmkstemp(TT.tempname); // Set permissions of output file fstat(TT.filein, &statbuf); fchmod(TT.fileout, statbuf.st_mode); TT.linenum = 0; TT.hunknum = 0; } } TT.hunknum++; continue; } // If we didn't continue above, discard this line. free(patchline); } finish_oldfile(); if (ENABLE_FEATURE_CLEAN_UP) { free(oldname); free(newname); } return TT.exitval; }
int man_main(int argc UNUSED_PARAM, char **argv) { parser_t *parser; const char *pager = ENABLE_LESS ? "less" : "more"; char *sec_list; char *cur_path, *cur_sect; char **man_path_list; int count_mp; int cur_mp; int opt, not_found; char *token[2]; opt_complementary = "-1"; /* at least one argument */ opt = getopt32(argv, "+aw"); argv += optind; sec_list = xstrdup("0p:1:1p:2:3:3p:4:5:6:7:8:9"); count_mp = 0; man_path_list = add_MANPATH(NULL, &count_mp, getenv("MANDATORY_MANPATH"+10) /* "MANPATH" */ ); if (!man_path_list) { /* default, may be overridden by /etc/man.conf */ man_path_list = xzalloc(2 * sizeof(man_path_list[0])); man_path_list[0] = (char*)"/usr/man"; /* count_mp stays 0. * Thus, man.conf will overwrite man_path_list[0] * if a path is defined there. */ } /* Parse man.conf[ig] or man_db.conf */ /* man version 1.6f uses man.config */ /* man-db implementation of man uses man_db.conf */ parser = config_open2("/etc/man.config", fopen_for_read); if (!parser) parser = config_open2("/etc/man.conf", fopen_for_read); if (!parser) parser = config_open2("/etc/man_db.conf", fopen_for_read); while (config_read(parser, token, 2, 0, "# \t", PARSE_NORMAL)) { if (!token[1]) continue; if (strcmp("DEFINE", token[0]) == 0) { if (is_prefixed_with("pager", token[1])) { pager = xstrdup(skip_whitespace(token[1]) + 5); } } else if (strcmp("MANDATORY_MANPATH"+10, token[0]) == 0 /* "MANPATH"? */ || strcmp("MANDATORY_MANPATH", token[0]) == 0 ) { man_path_list = add_MANPATH(man_path_list, &count_mp, token[1]); } if (strcmp("MANSECT", token[0]) == 0) { free(sec_list); sec_list = xstrdup(token[1]); } } config_close(parser); { /* environment overrides setting from man.config */ char *env_pager = getenv("MANPAGER"); if (!env_pager) env_pager = getenv("PAGER"); if (env_pager) pager = env_pager; } not_found = 0; do { /* for each argv[] */ int found = 0; cur_mp = 0; if (strchr(*argv, '/')) { found = show_manpage(pager, *argv, /*man:*/ 1, 0); goto check_found; } while ((cur_path = man_path_list[cur_mp++]) != NULL) { /* for each MANPATH */ cur_sect = sec_list; do { /* for each section */ char *next_sect = strchrnul(cur_sect, ':'); int sect_len = next_sect - cur_sect; char *man_filename; int cat0man1 = 0; /* Search for cat, then man page */ while (cat0man1 < 2) { int found_here; man_filename = xasprintf("%s/%s%.*s/%s.%.*s", cur_path, "cat\0man" + (cat0man1 * 4), sect_len, cur_sect, *argv, sect_len, cur_sect); found_here = show_manpage(pager, man_filename, cat0man1, 0); found |= found_here; cat0man1 += found_here + 1; free(man_filename); } if (found && !(opt & OPT_a)) goto next_arg; cur_sect = next_sect; while (*cur_sect == ':') cur_sect++; } while (*cur_sect); } check_found: if (!found) { bb_error_msg("no manual entry for '%s'", *argv); not_found = 1; } next_arg: argv++; } while (*argv); return not_found; }
/* 1) add a user: update_passwd(FILE, USER, REMAINING_PWLINE, NULL) only if CONFIG_ADDUSER=y and applet_name[0] == 'a' like in adduser 2) add a group: update_passwd(FILE, GROUP, REMAINING_GRLINE, NULL) only if CONFIG_ADDGROUP=y and applet_name[0] == 'a' like in addgroup 3) add a user to a group: update_passwd(FILE, GROUP, NULL, MEMBER) only if CONFIG_FEATURE_ADDUSER_TO_GROUP=y, applet_name[0] == 'a' like in addgroup and member != NULL 4) delete a user: update_passwd(FILE, USER, NULL, NULL) 5) delete a group: update_passwd(FILE, GROUP, NULL, NULL) 6) delete a user from a group: update_passwd(FILE, GROUP, NULL, MEMBER) only if CONFIG_FEATURE_DEL_USER_FROM_GROUP=y and member != NULL 7) change user's password: update_passwd(FILE, USER, NEW_PASSWD, NULL) only if CONFIG_PASSWD=y and applet_name[0] == 'p' like in passwd or if CONFIG_CHPASSWD=y and applet_name[0] == 'c' like in chpasswd 8) delete a user from all groups: update_passwd(FILE, NULL, NULL, MEMBER) This function does not validate the arguments fed to it so the calling program should take care of that. Returns number of lines changed, or -1 on error. */ int FAST_FUNC update_passwd(const char *filename, const char *name, const char *new_passwd, const char *member) { #if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP) #define member NULL #endif struct stat sb; struct flock lock; FILE *old_fp; FILE *new_fp; char *fnamesfx; char *sfx_char; char *name_colon; int old_fd; int new_fd; int i; int changed_lines; int ret = -1; /* failure */ /* used as a bool: "are we modifying /etc/shadow?" */ #if ENABLE_FEATURE_SHADOWPASSWDS const char *shadow = strstr(filename, "shadow"); #else # define shadow NULL #endif filename = xmalloc_follow_symlinks(filename); if (filename == NULL) return ret; if (name) check_selinux_update_passwd(name); /* New passwd file, "/etc/passwd+" for now */ fnamesfx = xasprintf("%s+", filename); sfx_char = &fnamesfx[strlen(fnamesfx)-1]; name_colon = xasprintf("%s:", name ? name : ""); if (shadow) old_fp = fopen(filename, "r+"); else old_fp = fopen_or_warn(filename, "r+"); if (!old_fp) { if (shadow) ret = 0; /* missing shadow is not an error */ goto free_mem; } old_fd = fileno(old_fp); selinux_preserve_fcontext(old_fd); /* Try to create "/etc/passwd+". Wait if it exists. */ i = 30; do { // FIXME: on last iteration try w/o O_EXCL but with O_TRUNC? new_fd = open(fnamesfx, O_WRONLY|O_CREAT|O_EXCL, 0600); if (new_fd >= 0) goto created; if (errno != EEXIST) break; usleep(100000); /* 0.1 sec */ } while (--i); bb_perror_msg("can't create '%s'", fnamesfx); goto close_old_fp; created: if (fstat(old_fd, &sb) == 0) { fchmod(new_fd, sb.st_mode & 0777); /* ignore errors */ fchown(new_fd, sb.st_uid, sb.st_gid); } errno = 0; new_fp = xfdopen_for_write(new_fd); /* Backup file is "/etc/passwd-" */ *sfx_char = '-'; /* Delete old backup */ i = (unlink(fnamesfx) && errno != ENOENT); /* Create backup as a hardlink to current */ if (i || link(filename, fnamesfx)) bb_perror_msg("warning: can't create backup copy '%s'", fnamesfx); *sfx_char = '+'; /* Lock the password file before updating */ lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; if (fcntl(old_fd, F_SETLK, &lock) < 0) bb_perror_msg("warning: can't lock '%s'", filename); lock.l_type = F_UNLCK; /* Read current password file, write updated /etc/passwd+ */ changed_lines = 0; while (1) { char *cp, *line; line = xmalloc_fgetline(old_fp); if (!line) /* EOF/error */ break; if (!name && member) { /* Delete member from all groups */ /* line is "GROUP:PASSWD:[member1[,member2]...]" */ unsigned member_len = strlen(member); char *list = strrchr(line, ':'); while (list) { list++; next_list_element: if (is_prefixed_with(list, member)) { char c; changed_lines++; c = list[member_len]; if (c == '\0') { if (list[-1] == ',') list--; *list = '\0'; break; } if (c == ',') { overlapping_strcpy(list, list + member_len + 1); goto next_list_element; } changed_lines--; } list = strchr(list, ','); } fprintf(new_fp, "%s\n", line); goto next; } cp = is_prefixed_with(line, name_colon); if (!cp) { fprintf(new_fp, "%s\n", line); goto next; } /* We have a match with "name:"... */ /* cp points past "name:" */ #if ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP if (member) { /* It's actually /etc/group+, not /etc/passwd+ */ if (ENABLE_FEATURE_ADDUSER_TO_GROUP && applet_name[0] == 'a' ) { /* Add user to group */ fprintf(new_fp, "%s%s%s\n", line, last_char_is(line, ':') ? "" : ",", member); changed_lines++; } else if (ENABLE_FEATURE_DEL_USER_FROM_GROUP /* && applet_name[0] == 'd' */ ) { /* Delete user from group */ char *tmp; const char *fmt = "%s"; /* find the start of the member list: last ':' */ cp = strrchr(line, ':'); /* cut it */ *cp++ = '\0'; /* write the cut line name:passwd:gid: * or name:!:: */ fprintf(new_fp, "%s:", line); /* parse the tokens of the member list */ tmp = cp; while ((cp = strsep(&tmp, ",")) != NULL) { if (strcmp(member, cp) != 0) { fprintf(new_fp, fmt, cp); fmt = ",%s"; } else { /* found member, skip it */ changed_lines++; } } fprintf(new_fp, "\n"); } } else #endif if ((ENABLE_PASSWD && applet_name[0] == 'p') || (ENABLE_CHPASSWD && applet_name[0] == 'c') ) { /* Change passwd */ cp = strchrnul(cp, ':'); /* move past old passwd */ if (shadow && *cp == ':') { /* /etc/shadow's field 3 (passwd change date) needs updating */ /* move past old change date */ cp = strchrnul(cp + 1, ':'); /* "name:" + "new_passwd" + ":" + "change date" + ":rest of line" */ fprintf(new_fp, "%s%s:%u%s\n", name_colon, new_passwd, (unsigned)(time(NULL)) / (24*60*60), cp); } else { /* "name:" + "new_passwd" + ":rest of line" */ fprintf(new_fp, "%s%s%s\n", name_colon, new_passwd, cp); } changed_lines++; } /* else delete user or group: skip the line */ next: free(line); } if (changed_lines == 0) { #if ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP if (member) { if (ENABLE_ADDGROUP && applet_name[0] == 'a') bb_error_msg("can't find %s in %s", name, filename); if (ENABLE_DELGROUP && applet_name[0] == 'd') bb_error_msg("can't find %s in %s", member, filename); } #endif if ((ENABLE_ADDUSER || ENABLE_ADDGROUP) && applet_name[0] == 'a' && !member ) { /* add user or group */ fprintf(new_fp, "%s%s\n", name_colon, new_passwd); changed_lines++; } } fcntl(old_fd, F_SETLK, &lock); /* We do want all of them to execute, thus | instead of || */ errno = 0; if ((ferror(old_fp) | fflush(new_fp) | fsync(new_fd) | fclose(new_fp)) || rename(fnamesfx, filename) ) { /* At least one of those failed */ bb_perror_nomsg(); goto unlink_new; } /* Success: ret >= 0 */ ret = changed_lines; unlink_new: if (ret < 0) unlink(fnamesfx); close_old_fp: fclose(old_fp); free_mem: free(fnamesfx); free((char *)filename); free(name_colon); return ret; }