static int x11_read_data(Context *c) { _cleanup_fclose_ FILE *f; char line[LINE_MAX]; bool in_section = false; int r; context_free_x11(c); f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re"); if (!f) return errno == ENOENT ? 0 : -errno; while (fgets(line, sizeof(line), f)) { char *l; char_array_0(line); l = strstrip(line); if (l[0] == 0 || l[0] == '#') continue; if (in_section && first_word(l, "Option")) { _cleanup_strv_free_ char **a = NULL; r = strv_split_quoted(&a, l, false); if (r < 0) return r; if (strv_length(a) == 3) { if (streq(a[1], "XkbLayout")) { free_and_replace(&c->x11_layout, a[2]); a[2] = NULL; } else if (streq(a[1], "XkbModel")) { free_and_replace(&c->x11_model, a[2]); a[2] = NULL; } else if (streq(a[1], "XkbVariant")) { free_and_replace(&c->x11_variant, a[2]); a[2] = NULL; } else if (streq(a[1], "XkbOptions")) { free_and_replace(&c->x11_options, a[2]); a[2] = NULL; } } } else if (!in_section && first_word(l, "Section")) { _cleanup_strv_free_ char **a = NULL; r = strv_split_quoted(&a, l, false); if (r < 0) return -ENOMEM; if (strv_length(a) == 2 && streq(a[1], "InputClass")) in_section = true; } else if (in_section && first_word(l, "EndSection")) in_section = false; } return 0; }
static void test_first_word(void) { assert_se(first_word("Hello", "")); assert_se(first_word("Hello", "Hello")); assert_se(first_word("Hello world", "Hello")); assert_se(first_word("Hello\tworld", "Hello")); assert_se(first_word("Hello\nworld", "Hello")); assert_se(first_word("Hello\rworld", "Hello")); assert_se(first_word("Hello ", "Hello")); assert_se(!first_word("Hello", "Hellooo")); assert_se(!first_word("Hello", "xxxxx")); assert_se(!first_word("Hellooo", "Hello")); }
/* Parses a string into the provided IrcMessage struct. Returns 1 on success, 0 on failure. */ int irc_parse_string(char *msgstr, int len, IrcMessage *msg) { //Copy msgstr into the full field char *fullcpy = malloc(sizeof(char) * strlen(msgstr)); msg->full = strcpy(fullcpy, msgstr); //Set up all other fields msg->prefix = NULL; msg->command = IRC_NONE; msg->argc = 0; msg->argv = NULL; //Make parser pointer char *ptr = msgstr; //If first char is : we have a prefix if(msgstr[0] == ':') { ptr++; msg->prefix = first_word(&ptr); } if(ptr == NULL) return 1; //First word after optional prefix is command char *commandstr = first_word(&ptr); //Convert command string to command const int cmdlen = strlen(commandstr); if(cmdlen <= 3 && strndigit(commandstr,cmdlen)) { msg->command = IRC_NUMERIC; } else if(!strncmp(commandstr,"PING",4)) { msg->command = IRC_PING; } else if(!strncmp(commandstr,"NOTICE",6) || !strncmp(commandstr,"PRIVMSG",7)) { msg->command = IRC_MESSAGE; } else if(!strncmp(commandstr,"ERROR",5)) { msg->command = IRC_ERROR; } else if(!strncmp(commandstr,"JOIN",4)) { msg->command = IRC_JOIN; } else { msg->command = IRC_OTHER; } if(ptr == NULL) return 1; //Read additional arguments msg->argv = read_args(&ptr, &(msg->argc)); return 1; }
int main(int ac, char **av) { if (ac == 2) first_word(av[1]); ft_putchar('\n'); return (0); }
/* Read the following fields from /proc/meminfo: * * NFS_Unstable * Writeback * Dirty * * Return true if the sum of these fields is greater than the previous * value input. For all other issues, report the failure and indicate that * the sync is not making progress. */ static bool sync_making_progress(unsigned long long *prev_dirty) { _cleanup_fclose_ FILE *f = NULL; unsigned long long val = 0; bool r = false; f = fopen("/proc/meminfo", "re"); if (!f) return log_warning_errno(errno, "Failed to open /proc/meminfo: %m"); for (;;) { _cleanup_free_ char *line = NULL; unsigned long long ull = 0; int q; q = read_line(f, LONG_LINE_MAX, &line); if (q < 0) return log_warning_errno(q, "Failed to parse /proc/meminfo: %m"); if (q == 0) break; if (!first_word(line, "NFS_Unstable:") && !first_word(line, "Writeback:") && !first_word(line, "Dirty:")) continue; errno = 0; if (sscanf(line, "%*s %llu %*s", &ull) != 1) { if (errno != 0) log_warning_errno(errno, "Failed to parse /proc/meminfo: %m"); else log_warning("Failed to parse /proc/meminfo"); return false; } val += ull; } r = *prev_dirty > val; *prev_dirty = val; return r; }
void InterpretedIC::replace(LookupResult result, klassOop receiver_klass) { // IC entries before modification - used for loging only Bytecodes::Code code_before = send_code(); oop word1_before = first_word(); oop word2_before = second_word(); int transition = 0; // modify IC guarantee(word2_before == receiver_klass, "klass should be the same"); if (result.is_empty()) { clear(); transition = 1; } else if (result.is_method()) { if (send_type() == Bytecodes::megamorphic_send) { set(send_code(), result.method(), receiver_klass); transition = 2; } else { // Please Fix this Robert // implement set_monomorphic(klass, method) clear(); transition = 3; } } else { if (send_type() == Bytecodes::megamorphic_send) { set(send_code(), oop(result.entry()), receiver_klass); transition = 4; } else { assert(result.is_entry(), "must be jump table entry"); // a jump table entry of a nmethod is found so let's update the current send set(Bytecodes::compiled_send_code_for(send_code()), oop(result.entry()), receiver_klass); transition = 5; } } // IC entries after modification - used for loging only Bytecodes::Code code_after = send_code(); oop word1_after = first_word(); oop word2_after = second_word(); // log modification LOG_EVENT3("InterpretedIC::replace: IC at 0x%x: entry for klass 0x%x replaced (transition %d)", this, receiver_klass, transition); LOG_EVENT3(" from (%s, 0x%x, 0x%x)", Bytecodes::name(code_before), word1_before, word2_before); LOG_EVENT3(" to (%s, 0x%x, 0x%x)", Bytecodes::name(code_after ), word1_after , word2_after ); }
symbolOop InterpretedIC::selector() const { oop fw = first_word(); if (fw->is_symbol()) { return symbolOop(fw); } else if (fw->is_method()) { return methodOop(fw)->selector(); } else { jumpTableEntry* e = jump_table_entry(); nmethod* nm = e->method(); assert(nm != NULL, "must have an nmethod"); return nm->key.selector(); } }
/** * Add markup for the detected hyphen * @param u the userdata * @param text the current text after the hyphen * @param len its length */ static void process_hyphen( userdata *u, XML_Char *text, int len ) { XML_Char *next = first_word(text,len); if ( next != NULL && strlen(next)>0 ) { char *force = "weak"; XML_Char *last = userdata_last_word(u); XML_Char *combined = combine_words(last,next); if ( (userdata_has_word(u,last) && userdata_has_word(u,next) && combined!=NULL && (!userdata_has_word(u,combined) ||userdata_has_hh_exception(u,combined))) || (strlen(next)>0&&isupper(next[0])) ) { force = "strong"; //printf("strong: %s-%s\n",last,next); } //else // printf("weak: %s\n",combined); // create a range to describe a hard hyphen char **atts = calloc(1,sizeof(char*)); if ( atts != NULL ) { range *r = range_new( 0, force, atts, userdata_hoffset(u) ); if ( r != NULL ) { dest_file *df = userdata_get_markup_dest( u, force ); userdata_set_hyphen_state(u,HYPHEN_NONE); range_set_len( r, 1 ); dest_file_enqueue( df, r ); } } else fprintf(stderr,"stripper: failed to create hyphen range\n"); if ( combined != NULL ) free( combined ); } if ( next != NULL ) free( next ); }
int x11_read_data(Context *c, sd_bus_message *m) { _cleanup_fclose_ FILE *f = NULL; bool in_section = false; char line[LINE_MAX]; struct stat st; usec_t t; int r; /* Do not try to re-read the file within single bus operation. */ if (m) { if (m == c->x11_cache) return 0; sd_bus_message_unref(c->x11_cache); c->x11_cache = sd_bus_message_ref(m); } if (stat("/etc/X11/xorg.conf.d/00-keyboard.conf", &st) < 0) { if (errno != ENOENT) return -errno; c->x11_mtime = USEC_INFINITY; context_free_x11(c); return 0; } /* If mtime is not changed, then we do not need to re-read */ t = timespec_load(&st.st_mtim); if (c->x11_mtime != USEC_INFINITY && t == c->x11_mtime) return 0; c->x11_mtime = t; context_free_x11(c); f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re"); if (!f) return -errno; while (fgets(line, sizeof(line), f)) { char *l; char_array_0(line); l = strstrip(line); if (IN_SET(l[0], 0, '#')) continue; if (in_section && first_word(l, "Option")) { _cleanup_strv_free_ char **a = NULL; r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_QUOTES); if (r < 0) return r; if (strv_length(a) == 3) { char **p = NULL; if (streq(a[1], "XkbLayout")) p = &c->x11_layout; else if (streq(a[1], "XkbModel")) p = &c->x11_model; else if (streq(a[1], "XkbVariant")) p = &c->x11_variant; else if (streq(a[1], "XkbOptions")) p = &c->x11_options; if (p) { free_and_replace(*p, a[2]); } } } else if (!in_section && first_word(l, "Section")) { _cleanup_strv_free_ char **a = NULL; r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_QUOTES); if (r < 0) return -ENOMEM; if (strv_length(a) == 2 && streq(a[1], "InputClass")) in_section = true; } else if (in_section && first_word(l, "EndSection")) in_section = false; } return 0; }
static int process(const char *controller, const char *path, Hashmap *a, Hashmap *b, unsigned iteration) { Group *g; int r; FILE *f = NULL; pid_t pid; unsigned n; assert(controller); assert(path); assert(a); g = hashmap_get(a, path); if (!g) { g = hashmap_get(b, path); if (!g) { g = new0(Group, 1); if (!g) return -ENOMEM; g->path = strdup(path); if (!g->path) { group_free(g); return -ENOMEM; } r = hashmap_put(a, g->path, g); if (r < 0) { group_free(g); return r; } } else { assert_se(hashmap_move_one(a, b, path) == 0); g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false; } } /* Regardless which controller, let's find the maximum number * of processes in any of it */ r = cg_enumerate_processes(controller, path, &f); if (r < 0) return r; n = 0; while (cg_read_pid(f, &pid) > 0) n++; fclose(f); if (n > 0) { if (g->n_tasks_valid) g->n_tasks = MAX(g->n_tasks, n); else g->n_tasks = n; g->n_tasks_valid = true; } if (streq(controller, "cpuacct")) { uint64_t new_usage; char *p, *v; struct timespec ts; r = cg_get_path(controller, path, "cpuacct.usage", &p); if (r < 0) return r; r = read_one_line_file(p, &v); free(p); if (r < 0) return r; r = safe_atou64(v, &new_usage); free(v); if (r < 0) return r; assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); if (g->cpu_iteration == iteration - 1) { uint64_t x, y; x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) - ((uint64_t) g->cpu_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->cpu_timestamp.tv_nsec); y = new_usage - g->cpu_usage; if (y > 0) { g->cpu_fraction = (double) y / (double) x; g->cpu_valid = true; } } g->cpu_usage = new_usage; g->cpu_timestamp = ts; g->cpu_iteration = iteration; } else if (streq(controller, "memory")) { char *p, *v; r = cg_get_path(controller, path, "memory.usage_in_bytes", &p); if (r < 0) return r; r = read_one_line_file(p, &v); free(p); if (r < 0) return r; r = safe_atou64(v, &g->memory); free(v); if (r < 0) return r; if (g->memory > 0) g->memory_valid = true; } else if (streq(controller, "blkio")) { char *p; uint64_t wr = 0, rd = 0; struct timespec ts; r = cg_get_path(controller, path, "blkio.io_service_bytes", &p); if (r < 0) return r; f = fopen(p, "re"); free(p); if (!f) return -errno; for (;;) { char line[LINE_MAX], *l; uint64_t k, *q; if (!fgets(line, sizeof(line), f)) break; l = strstrip(line); l += strcspn(l, WHITESPACE); l += strspn(l, WHITESPACE); if (first_word(l, "Read")) { l += 4; q = &rd; } else if (first_word(l, "Write")) { l += 5; q = ≀ } else continue; l += strspn(l, WHITESPACE); r = safe_atou64(l, &k); if (r < 0) continue; *q += k; } fclose(f); assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); if (g->io_iteration == iteration - 1) { uint64_t x, yr, yw; x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) - ((uint64_t) g->io_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->io_timestamp.tv_nsec); yr = rd - g->io_input; yw = wr - g->io_output; if (yr > 0 || yw > 0) { g->io_input_bps = (yr * 1000000000ULL) / x; g->io_output_bps = (yw * 1000000000ULL) / x; g->io_valid = true; } } g->io_input = rd; g->io_output = wr; g->io_timestamp = ts; g->io_iteration = iteration; } return 0; }
static int read_data_x11(void) { FILE *f; char line[LINE_MAX]; bool in_section = false; free_data_x11(); f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re"); if (!f) { if (errno == ENOENT) { #ifdef TARGET_FEDORA f = fopen("/etc/X11/xorg.conf.d/00-system-setup-keyboard.conf", "re"); if (!f) { if (errno == ENOENT) return 0; else return -errno; } #else return 0; #endif } else return -errno; } while (fgets(line, sizeof(line), f)) { char *l; char_array_0(line); l = strstrip(line); if (l[0] == 0 || l[0] == '#') continue; if (in_section && first_word(l, "Option")) { char **a; a = strv_split_quoted(l); if (!a) { fclose(f); return -ENOMEM; } if (strv_length(a) == 3) { if (streq(a[1], "XkbLayout")) { free(state.x11_layout); state.x11_layout = a[2]; a[2] = NULL; } else if (streq(a[1], "XkbModel")) { free(state.x11_model); state.x11_model = a[2]; a[2] = NULL; } else if (streq(a[1], "XkbVariant")) { free(state.x11_variant); state.x11_variant = a[2]; a[2] = NULL; } else if (streq(a[1], "XkbOptions")) { free(state.x11_options); state.x11_options = a[2]; a[2] = NULL; } } strv_free(a); } else if (!in_section && first_word(l, "Section")) { char **a; a = strv_split_quoted(l); if (!a) { fclose(f); return -ENOMEM; } if (strv_length(a) == 2 && streq(a[1], "InputClass")) in_section = true; strv_free(a); } else if (in_section && first_word(l, "EndSection")) in_section = false; } fclose(f); return 0; }
/* Parse a single logical line */ static int parse_line( const char* unit, const char *filename, unsigned line, const char *sections, ConfigItemLookup lookup, const void *table, ConfigParseFlags flags, char **section, unsigned *section_line, bool *section_ignored, char *l, void *userdata) { char *e, *include; assert(filename); assert(line > 0); assert(lookup); assert(l); l = strstrip(l); if (!*l) return 0; if (strchr(COMMENTS "\n", *l)) return 0; include = first_word(l, ".include"); if (include) { _cleanup_free_ char *fn = NULL; /* .includes are a bad idea, we only support them here * for historical reasons. They create cyclic include * problems and make it difficult to detect * configuration file changes with an easy * stat(). Better approaches, such as .d/ drop-in * snippets exist. * * Support for them should be eventually removed. */ if (!(flags & CONFIG_PARSE_ALLOW_INCLUDE)) { log_syntax(unit, LOG_ERR, filename, line, 0, ".include not allowed here. Ignoring."); return 0; } log_syntax(unit, LOG_WARNING, filename, line, 0, ".include directives are deprecated, and support for them will be removed in a future version of systemd. " "Please use drop-in files instead."); fn = file_in_same_dir(filename, strstrip(include)); if (!fn) return -ENOMEM; return config_parse(unit, fn, NULL, sections, lookup, table, flags, userdata); } if (!utf8_is_valid(l)) return log_syntax_invalid_utf8(unit, LOG_WARNING, filename, line, l); if (*l == '[') { size_t k; char *n; k = strlen(l); assert(k > 0); if (l[k-1] != ']') { log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid section header '%s'", l); return -EBADMSG; } n = strndup(l+1, k-2); if (!n) return -ENOMEM; if (sections && !nulstr_contains(sections, n)) { if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(n, "X-")) log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n); free(n); *section = mfree(*section); *section_line = 0; *section_ignored = true; } else { free_and_replace(*section, n); *section_line = line; *section_ignored = false; } return 0; } if (sections && !*section) { if (!(flags & CONFIG_PARSE_RELAXED) && !*section_ignored) log_syntax(unit, LOG_WARNING, filename, line, 0, "Assignment outside of section. Ignoring."); return 0; } e = strchr(l, '='); if (!e) { log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing '='."); return -EINVAL; } *e = 0; e++; return next_assignment(unit, filename, line, lookup, table, *section, *section_line, strstrip(l), strstrip(e), flags, userdata); }
static void child_process(entry *e) { int stdin_pipe[2], stdout_pipe[2]; char * volatile input_data; char *homedir, *usernm, * volatile mailto; int children = 0; Debug(DPROC, ("[%ld] child_process('%s')\n", (long)getpid(), e->cmd)); setproctitle("running job"); /* discover some useful and important environment settings */ usernm = e->pwd->pw_name; mailto = env_get("MAILTO", e->envp); /* our parent is watching for our death by catching SIGCHLD. we * do not care to watch for our children's deaths this way -- we * use wait() explicitly. so we have to reset the signal (which * was inherited from the parent). */ (void) signal(SIGCHLD, SIG_DFL); /* create some pipes to talk to our future child */ if (pipe(stdin_pipe) == -1) /* child's stdin */ log_it("CRON", getpid(), "error", "create child stdin pipe"); if (pipe(stdout_pipe) == -1) /* child's stdout */ log_it("CRON", getpid(), "error", "create child stdout pipe"); /* since we are a forked process, we can diddle the command string * we were passed -- nobody else is going to use it again, right? * * if a % is present in the command, previous characters are the * command, and subsequent characters are the additional input to * the command. An escaped % will have the escape character stripped * from it. Subsequent %'s will be transformed into newlines, * but that happens later. */ /*local*/{ int escaped = FALSE; int ch; char *p; /* translation: * \% -> % * % -> end of command, following is command input. * \x -> \x for all x != % */ input_data = p = e->cmd; while ((ch = *input_data++) != '\0') { if (escaped) { if (ch != '%') *p++ = '\\'; } else { if (ch == '%') { break; } } if (!(escaped = (ch == '\\'))) { *p++ = ch; } } if (ch == '\0') { /* move pointer back, so that code below * won't think we encountered % sequence */ input_data--; } if (escaped) *p++ = '\\'; *p = '\0'; } /* fork again, this time so we can exec the user's command. */ switch (vfork()) { case -1: log_it("CRON", getpid(), "error", "can't vfork"); exit(ERROR_EXIT); /*NOTREACHED*/ case 0: Debug(DPROC, ("[%ld] grandchild process vfork()'ed\n", (long)getpid())); /* write a log message. we've waited this long to do it * because it was not until now that we knew the PID that * the actual user command shell was going to get and the * PID is part of the log message. */ if ((e->flags & DONT_LOG) == 0) { char *x = mkprints(e->cmd, strlen(e->cmd)); log_it(usernm, getpid(), "CMD START", x); free(x); } /* that's the last thing we'll log. close the log files. */ log_close(); /* get new pgrp, void tty, etc. */ if (setsid() == -1) syslog(LOG_ERR, "setsid() failure: %m"); /* close the pipe ends that we won't use. this doesn't affect * the parent, who has to read and write them; it keeps the * kernel from recording us as a potential client TWICE -- * which would keep it from sending SIGPIPE in otherwise * appropriate circumstances. */ (void)close(stdin_pipe[WRITE_PIPE]); (void)close(stdout_pipe[READ_PIPE]); /* grandchild process. make std{in,out} be the ends of * pipes opened by our daddy; make stderr go to stdout. */ if (stdin_pipe[READ_PIPE] != STDIN) { (void)dup2(stdin_pipe[READ_PIPE], STDIN); (void)close(stdin_pipe[READ_PIPE]); } if (stdout_pipe[WRITE_PIPE] != STDOUT) { (void)dup2(stdout_pipe[WRITE_PIPE], STDOUT); (void)close(stdout_pipe[WRITE_PIPE]); } (void)dup2(STDOUT, STDERR); /* set our directory, uid and gid. Set gid first, since once * we set uid, we've lost root privledges. */ #ifdef LOGIN_CAP { #ifdef BSD_AUTH auth_session_t *as; #endif login_cap_t *lc; char *p; if ((lc = login_getclass(e->pwd->pw_class)) == NULL) { warnx("unable to get login class for `%s'", e->pwd->pw_name); _exit(ERROR_EXIT); } if (setusercontext(lc, e->pwd, e->pwd->pw_uid, LOGIN_SETALL) < 0) { warnx("setusercontext failed for `%s'", e->pwd->pw_name); _exit(ERROR_EXIT); } #ifdef BSD_AUTH as = auth_open(); if (as == NULL || auth_setpwd(as, e->pwd) != 0) { warn("can't malloc"); _exit(ERROR_EXIT); } if (auth_approval(as, lc, usernm, "cron") <= 0) { warnx("approval failed for `%s'", e->pwd->pw_name); _exit(ERROR_EXIT); } auth_close(as); #endif /* BSD_AUTH */ login_close(lc); /* If no PATH specified in crontab file but * we just added one via login.conf, add it to * the crontab environment. */ if (env_get("PATH", e->envp) == NULL) { if ((p = getenv("PATH")) != NULL) e->envp = env_set(e->envp, p); } } #else if (setgid(e->pwd->pw_gid) != 0) { syslog(LOG_ERR, "setgid(%d) failed for %s: %m", e->pwd->pw_gid, e->pwd->pw_name); _exit(ERROR_EXIT); } if (initgroups(usernm, e->pwd->pw_gid) != 0) { syslog(LOG_ERR, "initgroups(%s, %d) failed for %s: %m", usernm, e->pwd->pw_gid, e->pwd->pw_name); _exit(ERROR_EXIT); } #if (defined(BSD)) && (BSD >= 199103) if (setlogin(usernm) < 0) { syslog(LOG_ERR, "setlogin(%s) failure for %s: %m", usernm, e->pwd->pw_name); _exit(ERROR_EXIT); } #endif /* BSD */ if (setuid(e->pwd->pw_uid) != 0) { syslog(LOG_ERR, "setuid(%d) failed for %s: %m", e->pwd->pw_uid, e->pwd->pw_name); _exit(ERROR_EXIT); } /* we aren't root after this... */ #endif /* LOGIN_CAP */ homedir = env_get("HOME", e->envp); if (chdir(homedir) != 0) { syslog(LOG_ERR, "chdir(%s) $HOME failed for %s: %m", homedir, e->pwd->pw_name); _exit(ERROR_EXIT); } #ifdef USE_SIGCHLD /* our grandparent is watching for our death by catching * SIGCHLD. the parent is ignoring SIGCHLD's; we want * to restore default behaviour. */ (void) signal(SIGCHLD, SIG_DFL); #endif (void) signal(SIGHUP, SIG_DFL); /* * Exec the command. */ { char *shell = env_get("SHELL", e->envp); # if DEBUGGING if (DebugFlags & DTEST) { (void)fprintf(stderr, "debug DTEST is on, not exec'ing command.\n"); (void)fprintf(stderr, "\tcmd='%s' shell='%s'\n", e->cmd, shell); _exit(OK_EXIT); } # endif /*DEBUGGING*/ (void)execle(shell, shell, "-c", e->cmd, NULL, e->envp); warn("execl: couldn't exec `%s'", shell); _exit(ERROR_EXIT); } break; default: /* parent process */ break; } children++; /* middle process, child of original cron, parent of process running * the user's command. */ Debug(DPROC, ("[%ld] child continues, closing pipes\n",(long)getpid())); /* close the ends of the pipe that will only be referenced in the * grandchild process... */ (void)close(stdin_pipe[READ_PIPE]); (void)close(stdout_pipe[WRITE_PIPE]); /* * write, to the pipe connected to child's stdin, any input specified * after a % in the crontab entry. while we copy, convert any * additional %'s to newlines. when done, if some characters were * written and the last one wasn't a newline, write a newline. * * Note that if the input data won't fit into one pipe buffer (2K * or 4K on most BSD systems), and the child doesn't read its stdin, * we would block here. thus we must fork again. */ if (*input_data && fork() == 0) { FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w"); int need_newline = FALSE; int escaped = FALSE; int ch; Debug(DPROC, ("[%ld] child2 sending data to grandchild\n", (long)getpid())); /* close the pipe we don't use, since we inherited it and * are part of its reference count now. */ (void)close(stdout_pipe[READ_PIPE]); /* translation: * \% -> % * % -> \n * \x -> \x for all x != % */ while ((ch = *input_data++) != '\0') { if (escaped) { if (ch != '%') (void)putc('\\', out); } else { if (ch == '%') ch = '\n'; } if (!(escaped = (ch == '\\'))) { (void)putc(ch, out); need_newline = (ch != '\n'); } } if (escaped) (void)putc('\\', out); if (need_newline) (void)putc('\n', out); /* close the pipe, causing an EOF condition. fclose causes * stdin_pipe[WRITE_PIPE] to be closed, too. */ (void)fclose(out); Debug(DPROC, ("[%ld] child2 done sending to grandchild\n", (long)getpid())); exit(0); } /* close the pipe to the grandkiddie's stdin, since its wicked uncle * ernie back there has it open and will close it when he's done. */ (void)close(stdin_pipe[WRITE_PIPE]); children++; /* * read output from the grandchild. it's stderr has been redirected to * it's stdout, which has been redirected to our pipe. if there is any * output, we'll be mailing it to the user whose crontab this is... * when the grandchild exits, we'll get EOF. */ Debug(DPROC, ("[%ld] child reading output from grandchild\n", (long)getpid())); /*local*/{ FILE *in = fdopen(stdout_pipe[READ_PIPE], "r"); int ch = getc(in); if (ch != EOF) { FILE *mail = NULL; int bytes = 1; int status = 0; Debug(DPROC|DEXT, ("[%ld] got data (%x:%c) from grandchild\n", (long)getpid(), ch, ch)); /* get name of recipient. this is MAILTO if set to a * valid local username; USER otherwise. */ if (mailto) { /* MAILTO was present in the environment */ if (!*mailto) { /* ... but it's empty. set to NULL */ mailto = NULL; } } else { /* MAILTO not present, set to USER. */ mailto = usernm; } /* if we are supposed to be mailing, MAILTO will * be non-NULL. only in this case should we set * up the mail command and subjects and stuff... */ if (mailto && safe_p(usernm, mailto)) { char **env; char mailcmd[MAX_COMMAND]; char hostname[MAXHOSTNAMELEN + 1]; (void)gethostname(hostname, MAXHOSTNAMELEN); if (strlens(MAILFMT, MAILARG, NULL) + 1 >= sizeof mailcmd) { warnx("mailcmd too long"); (void) _exit(ERROR_EXIT); } (void)snprintf(mailcmd, sizeof(mailcmd), MAILFMT, MAILARG); if (!(mail = cron_popen(mailcmd, "w", e->pwd))) { warn("cannot run `%s'", mailcmd); (void) _exit(ERROR_EXIT); } (void)fprintf(mail, "From: root (Cron Daemon)\n"); (void)fprintf(mail, "To: %s\n", mailto); (void)fprintf(mail, "Subject: Cron <%s@%s> %s\n", usernm, first_word(hostname, "."), e->cmd); (void)fprintf(mail, "Auto-Submitted: auto-generated\n"); #ifdef MAIL_DATE (void)fprintf(mail, "Date: %s\n", arpadate(&StartTime)); #endif /*MAIL_DATE*/ for (env = e->envp; *env; env++) (void)fprintf(mail, "X-Cron-Env: <%s>\n", *env); (void)fprintf(mail, "\n"); /* this was the first char from the pipe */ (void)putc(ch, mail); } /* we have to read the input pipe no matter whether * we mail or not, but obviously we only write to * mail pipe if we ARE mailing. */ while (EOF != (ch = getc(in))) { bytes++; if (mailto) (void)putc(ch, mail); } /* only close pipe if we opened it -- i.e., we're * mailing... */ if (mailto) { Debug(DPROC, ("[%ld] closing pipe to mail\n", (long)getpid())); /* Note: the pclose will probably see * the termination of the grandchild * in addition to the mail process, since * it (the grandchild) is likely to exit * after closing its stdout. */ status = cron_pclose(mail); } /* if there was output and we could not mail it, * log the facts so the poor user can figure out * what's going on. */ if (mailto && status) { char buf[MAX_TEMPSTR]; (void)snprintf(buf, sizeof(buf), "mailed %d byte%s of output but got status 0x%04x\n", bytes, (bytes==1)?"":"s", status); log_it(usernm, getpid(), "MAIL", buf); } } /*if data from grandchild*/ Debug(DPROC, ("[%ld] got EOF from grandchild\n", (long)getpid())); (void)fclose(in); /* also closes stdout_pipe[READ_PIPE] */ } /* wait for children to die. */ for (; children > 0; children--) { WAIT_T waiter; PID_T pid; Debug(DPROC, ("[%ld] waiting for grandchild #%d to finish\n", (long)getpid(), children)); while ((pid = wait(&waiter)) < OK && errno == EINTR) ; if (pid < OK) { Debug(DPROC, ("[%ld] no more grandchildren--mail written?\n", (long)getpid())); break; } Debug(DPROC, ("[%ld] grandchild #%ld finished, status=%04x", (long)getpid(), (long)pid, WEXITSTATUS(waiter))); if (WIFSIGNALED(waiter) && WCOREDUMP(waiter)) Debug(DPROC, (", dumped core")); Debug(DPROC, ("\n")); } /* Log the time when we finished deadling with the job */ /*local*/{ char *x = mkprints(e->cmd, strlen(e->cmd)); log_it(usernm, getpid(), "CMD FINISH", x); free(x); } }
void InterpretedIC::cleanup() { if (is_empty()) return; // Nothing to cleanup switch (send_type()) { case Bytecodes::accessor_send: // fall through case Bytecodes::primitive_send: // fall through case Bytecodes::predicted_send: // fall through case Bytecodes::interpreted_send: { // check if the interpreted send should be replaced by a compiled send klassOop receiver_klass = klassOop(second_word()); assert(receiver_klass->is_klass(), "receiver klass must be a klass"); methodOop method = methodOop(first_word()); assert(method->is_method(), "first word in interpreter IC must be method"); if (!Bytecodes::is_super_send(send_code())) { // super sends cannot be handled since the sending method holder is unknown at this point. LookupKey key(receiver_klass, selector()); LookupResult result = lookupCache::lookup(&key); if (!result.matches(method)) { replace(result, receiver_klass); } } } break; case Bytecodes::compiled_send: { // check if the current compiled send is valid klassOop receiver_klass = klassOop(second_word()); assert(receiver_klass->is_klass(), "receiver klass must be a klass"); jumpTableEntry* entry = (jumpTableEntry*) first_word(); nmethod* nm = entry->method(); LookupResult result = lookupCache::lookup(&nm->key); if (!result.matches(nm)) { replace(result, receiver_klass); } } break; case Bytecodes::megamorphic_send: // Note that with the current definition of is_empty() // this will not be called for normal megamorphic sends // since they store only the selector. { klassOop receiver_klass = klassOop(second_word()); if (first_word()->is_smi()) { jumpTableEntry* entry = (jumpTableEntry*) first_word(); nmethod* nm = entry->method(); LookupResult result = lookupCache::lookup(&nm->key); if (!result.matches(nm)) { replace(result, receiver_klass); } } else { methodOop method = methodOop(first_word()); assert(method->is_method(), "first word in interpreter IC must be method"); if (!Bytecodes::is_super_send(send_code())) { // super sends cannot be handled since the sending method holder is unknown at this point. LookupKey key(receiver_klass, selector()); LookupResult result = lookupCache::lookup(&key); if (!result.matches(method)) { replace(result, receiver_klass); } } } } break; case Bytecodes::polymorphic_send: { // %implementation note: // when cleaning up we can always preserve the old pic since the // the only transitions are: // (compiled -> compiled) // (compiled -> interpreted) // (interpreted -> compiled) // in case of a super send we do not have to cleanup because // no nmethods are compiled for super sends. if (!Bytecodes::is_super_send(send_code())) { objArrayOop pic = pic_array(); for (int index = pic->length(); index > 0; index -= 2) { klassOop klass = klassOop(pic->obj_at(index)); assert(klass->is_klass(), "receiver klass must be klass"); oop first = pic->obj_at(index-1); if (first->is_smi()) { jumpTableEntry* entry = (jumpTableEntry*) first; nmethod* nm = entry->method(); LookupResult result = lookupCache::lookup(&nm->key); if (!result.matches(nm)) { pic->obj_at_put(index-1, result.value()); } } else { methodOop method = methodOop(first); assert(method->is_method(), "first word in interpreter IC must be method"); LookupKey key(klass, selector()); LookupResult result = lookupCache::lookup(&key); if (!result.matches(method)) { pic->obj_at_put(index-1, result.value()); } } } } } } }
jumpTableEntry* InterpretedIC::jump_table_entry() const { assert(send_type() == Bytecodes::compiled_send || send_type() == Bytecodes::megamorphic_send, "must be a compiled call"); assert(first_word()->is_smi(), "must be smi"); return (jumpTableEntry*) first_word(); }
int manager_read_resolv_conf(Manager *m) { _cleanup_fclose_ FILE *f = NULL; struct stat st, own; char line[LINE_MAX]; usec_t t; int r; assert(m); /* Reads the system /etc/resolv.conf, if it exists and is not * symlinked to our own resolv.conf instance */ if (!m->read_resolv_conf) return 0; r = stat("/etc/resolv.conf", &st); if (r < 0) { if (errno == ENOENT) return 0; r = log_warning_errno(errno, "Failed to stat /etc/resolv.conf: %m"); goto clear; } /* Have we already seen the file? */ t = timespec_load(&st.st_mtim); if (t == m->resolv_conf_mtime) return 0; /* Is it symlinked to our own file? */ if (stat(PRIVATE_RESOLV_CONF, &own) >= 0 && st.st_dev == own.st_dev && st.st_ino == own.st_ino) return 0; f = fopen("/etc/resolv.conf", "re"); if (!f) { if (errno == ENOENT) return 0; r = log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m"); goto clear; } if (fstat(fileno(f), &st) < 0) { r = log_error_errno(errno, "Failed to stat open file: %m"); goto clear; } dns_server_mark_all(m->dns_servers); dns_search_domain_mark_all(m->search_domains); FOREACH_LINE(line, f, r = -errno; goto clear) { const char *a; char *l; l = strstrip(line); if (IN_SET(*l, '#', ';')) continue; a = first_word(l, "nameserver"); if (a) { r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_SYSTEM, a); if (r < 0) log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring.", a); continue; } a = first_word(l, "domain"); if (!a) /* We treat "domain" lines, and "search" lines as equivalent, and add both to our list. */ a = first_word(l, "search"); if (a) { r = manager_parse_search_domains_and_warn(m, a); if (r < 0) log_warning_errno(r, "Failed to parse search domain string '%s', ignoring.", a); } } m->resolv_conf_mtime = t; /* Flush out all servers and search domains that are still * marked. Those are then ones that didn't appear in the new * /etc/resolv.conf */ dns_server_unlink_marked(m->dns_servers); dns_search_domain_unlink_marked(m->search_domains); /* Whenever /etc/resolv.conf changes, start using the first * DNS server of it. This is useful to deal with broken * network managing implementations (like NetworkManager), * that when connecting to a VPN place both the VPN DNS * servers and the local ones in /etc/resolv.conf. Without * resetting the DNS server to use back to the first entry we * will continue to use the local one thus being unable to * resolve VPN domains. */ manager_set_dns_server(m, m->dns_servers); /* Unconditionally flush the cache when /etc/resolv.conf is * modified, even if the data it contained was completely * identical to the previous version we used. We do this * because altering /etc/resolv.conf is typically done when * the network configuration changes, and that should be * enough to flush the global unicast DNS cache. */ if (m->unicast_scope) dns_cache_flush(&m->unicast_scope->cache); /* If /etc/resolv.conf changed, make sure to forget everything we learned about the DNS servers. After all we * might now talk to a very different DNS server that just happens to have the same IP address as an old one * (think 192.168.1.1). */ dns_server_reset_features_all(m->dns_servers); return 0; clear: dns_server_unlink_all(m->dns_servers); dns_search_domain_unlink_all(m->search_domains); return r; }
static int process( const char *controller, const char *path, Hashmap *a, Hashmap *b, unsigned iteration, Group **ret) { Group *g; int r; assert(controller); assert(path); assert(a); g = hashmap_get(a, path); if (!g) { g = hashmap_get(b, path); if (!g) { g = new0(Group, 1); if (!g) return -ENOMEM; g->path = strdup(path); if (!g->path) { group_free(g); return -ENOMEM; } r = hashmap_put(a, g->path, g); if (r < 0) { group_free(g); return r; } } else { r = hashmap_move_one(a, b, path); if (r < 0) return r; g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false; } } if (streq(controller, SYSTEMD_CGROUP_CONTROLLER) && IN_SET(arg_count, COUNT_ALL_PROCESSES, COUNT_USERSPACE_PROCESSES)) { _cleanup_fclose_ FILE *f = NULL; pid_t pid; r = cg_enumerate_processes(controller, path, &f); if (r == -ENOENT) return 0; if (r < 0) return r; g->n_tasks = 0; while (cg_read_pid(f, &pid) > 0) { if (arg_count == COUNT_USERSPACE_PROCESSES && is_kernel_thread(pid) > 0) continue; g->n_tasks++; } if (g->n_tasks > 0) g->n_tasks_valid = true; } else if (streq(controller, "pids") && arg_count == COUNT_PIDS) { _cleanup_free_ char *p = NULL, *v = NULL; r = cg_get_path(controller, path, "pids.current", &p); if (r < 0) return r; r = read_one_line_file(p, &v); if (r == -ENOENT) return 0; if (r < 0) return r; r = safe_atou64(v, &g->n_tasks); if (r < 0) return r; if (g->n_tasks > 0) g->n_tasks_valid = true; } else if (streq(controller, "cpu") || streq(controller, "cpuacct")) { _cleanup_free_ char *p = NULL, *v = NULL; uint64_t new_usage; nsec_t timestamp; if (cg_all_unified() > 0) { const char *keys[] = { "usage_usec", NULL }; _cleanup_free_ char *val = NULL; if (!streq(controller, "cpu")) return 0; r = cg_get_keyed_attribute("cpu", path, "cpu.stat", keys, &val); if (r == -ENOENT) return 0; if (r < 0) return r; r = safe_atou64(val, &new_usage); if (r < 0) return r; new_usage *= NSEC_PER_USEC; } else { if (!streq(controller, "cpuacct")) return 0; r = cg_get_path(controller, path, "cpuacct.usage", &p); if (r < 0) return r; r = read_one_line_file(p, &v); if (r == -ENOENT) return 0; if (r < 0) return r; r = safe_atou64(v, &new_usage); if (r < 0) return r; } timestamp = now_nsec(CLOCK_MONOTONIC); if (g->cpu_iteration == iteration - 1 && (nsec_t) new_usage > g->cpu_usage) { nsec_t x, y; x = timestamp - g->cpu_timestamp; if (x < 1) x = 1; y = (nsec_t) new_usage - g->cpu_usage; g->cpu_fraction = (double) y / (double) x; g->cpu_valid = true; } g->cpu_usage = (nsec_t) new_usage; g->cpu_timestamp = timestamp; g->cpu_iteration = iteration; } else if (streq(controller, "memory")) { _cleanup_free_ char *p = NULL, *v = NULL; if (cg_all_unified() <= 0) r = cg_get_path(controller, path, "memory.usage_in_bytes", &p); else r = cg_get_path(controller, path, "memory.current", &p); if (r < 0) return r; r = read_one_line_file(p, &v); if (r == -ENOENT) return 0; if (r < 0) return r; r = safe_atou64(v, &g->memory); if (r < 0) return r; if (g->memory > 0) g->memory_valid = true; } else if ((streq(controller, "io") && cg_all_unified() > 0) || (streq(controller, "blkio") && cg_all_unified() <= 0)) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *p = NULL; bool unified = cg_all_unified() > 0; uint64_t wr = 0, rd = 0; nsec_t timestamp; r = cg_get_path(controller, path, unified ? "io.stat" : "blkio.io_service_bytes", &p); if (r < 0) return r; f = fopen(p, "re"); if (!f) { if (errno == ENOENT) return 0; return -errno; } for (;;) { char line[LINE_MAX], *l; uint64_t k, *q; if (!fgets(line, sizeof(line), f)) break; /* Trim and skip the device */ l = strstrip(line); l += strcspn(l, WHITESPACE); l += strspn(l, WHITESPACE); if (unified) { while (!isempty(l)) { if (sscanf(l, "rbytes=%" SCNu64, &k)) rd += k; else if (sscanf(l, "wbytes=%" SCNu64, &k)) wr += k; l += strcspn(l, WHITESPACE); l += strspn(l, WHITESPACE); } } else { if (first_word(l, "Read")) { l += 4; q = &rd; } else if (first_word(l, "Write")) { l += 5; q = ≀ } else continue; l += strspn(l, WHITESPACE); r = safe_atou64(l, &k); if (r < 0) continue; *q += k; } } timestamp = now_nsec(CLOCK_MONOTONIC); if (g->io_iteration == iteration - 1) { uint64_t x, yr, yw; x = (uint64_t) (timestamp - g->io_timestamp); if (x < 1) x = 1; if (rd > g->io_input) yr = rd - g->io_input; else yr = 0; if (wr > g->io_output) yw = wr - g->io_output; else yw = 0; if (yr > 0 || yw > 0) { g->io_input_bps = (yr * 1000000000ULL) / x; g->io_output_bps = (yw * 1000000000ULL) / x; g->io_valid = true; } } g->io_input = rd; g->io_output = wr; g->io_timestamp = timestamp; g->io_iteration = iteration; } if (ret) *ret = g; return 0; }
static void child_process (entry * e, user * u) { int stdin_pipe[2], stdout_pipe[2]; register char *input_data; char *usernm, *mailto; int verbose; /* Put lots of info about job in the output mail msg */ int children = 0; Debug (DPROC, ("[%d] child_process('%s')\n", getpid (), e->cmd)); /* mark ourselves as different to PS command watchers by upshifting * our process name. This has no effect on some kernels. */ /*local */ { register char *pch; for (pch = ProcessName; *pch; pch++) *pch = MkUpper (*pch); } /* discover some useful and important environment settings */ usernm = env_get ("LOGNAME", e->envp); mailto = env_get ("MAILTO", e->envp); /* The environment variable CRON_VERBOSE is normally set in the crontab itself. It means user wants lots of info in the mail message to the cron job owner, including the full environment. */ if (env_get ("CRON_VERBOSE", e->envp)) verbose = 1; else verbose = 0; /* Check for arguments */ if (mailto) { const char *end; /* These chars have to match those cron_popen() * uses to split the command string */ mailto += strspn (mailto, " \t\n"); end = mailto + strcspn (mailto, " \t\n"); if (*mailto == '-' || *end != '\0') { printf ("Bad Mailto karma.\n"); log_it ("CRON", getpid (), "error", "bad mailto"); mailto = NULL; } } #ifdef USE_SIGCHLD /* our parent is watching for our death by catching SIGCHLD. we * do not care to watch for our children's deaths this way -- we * use wait() explictly. so we have to disable the signal (which * was inherited from the parent). */ (void) signal (SIGCHLD, SIG_IGN); #else /* on system-V systems, we are ignoring SIGCHLD. we have to stop * ignoring it now or the wait() in cron_pclose() won't work. * because of this, we have to wait() for our children here, as well. */ (void) signal (SIGCHLD, SIG_DFL); #endif /* USE_SIGCHLD */ /* create some pipes to talk to our future child */ pipe (stdin_pipe); /* child's stdin */ pipe (stdout_pipe); /* child's stdout */ /* since we are a forked process, we can diddle the command string * we were passed -- nobody else is going to use it again, right? * * if a % is present in the command, previous characters are the * command, and subsequent characters are the additional input to * the command. Subsequent %'s will be transformed into newlines, * but that happens later. */ /*local */ { register int escaped = FALSE; register int ch; for (input_data = e->cmd; (ch = *input_data); input_data++) { if (escaped) { escaped = FALSE; continue; } if (ch == '\\') { escaped = TRUE; continue; } if (ch == '%') { *input_data++ = '\0'; break; } } } /* fork again, this time so we can exec the user's command. */ switch (vfork ()) { case -1: log_it ("CRON", getpid (), "error", "can't vfork"); exit (ERROR_EXIT); /*NOTREACHED*/ case 0: Debug (DPROC, ("[%d] grandchild process Vfork()'ed\n", getpid ())); /* write a log message. we've waited this long to do it * because it was not until now that we knew the PID that * the actual user command shell was going to get and the * PID is part of the log message. */ /*local */ { char *x = mkprints ((u_char *) e->cmd, strlen (e->cmd)); log_it (usernm, getpid (), "CMD", x); free (x); } /* that's the last thing we'll log. close the log files. */ #ifdef HAVE_SYSLOG_H if (log_syslog) closelog (); #endif /* get new pgrp, void tty, etc. */ (void) setsid (); /* close the pipe ends that we won't use. this doesn't affect * the parent, who has to read and write them; it keeps the * kernel from recording us as a potential client TWICE -- * which would keep it from sending SIGPIPE in otherwise * appropriate circumstances. */ close (stdin_pipe[WRITE_PIPE]); close (stdout_pipe[READ_PIPE]); /* grandchild process. make std{in,out} be the ends of * pipes opened by our daddy; make stderr go to stdout. */ close (STDIN); dup2 (stdin_pipe[READ_PIPE], STDIN); close (STDOUT); dup2 (stdout_pipe[WRITE_PIPE], STDOUT); close (STDERR); dup2 (STDOUT, STDERR); /* close the pipes we just dup'ed. The resources will remain. */ close (stdin_pipe[READ_PIPE]); close (stdout_pipe[WRITE_PIPE]); /* set our directory, uid and gid. Set gid first, since once * we set uid, we've lost root privledges. */ setgid (e->gid); #ifdef HAVE_INITGROUPS initgroups (env_get ("LOGNAME", e->envp), e->gid); #endif setuid (e->uid); /* we aren't root after this... */ chdir (env_get ("HOME", e->envp)); /* exec the command. */ { char *shell = env_get ("SHELL", e->envp); #if DEBUGGING if (DebugFlags & DTEST) { fprintf (stderr, "debug DTEST is on, not exec'ing command.\n"); fprintf (stderr, "\tcmd='%s' shell='%s'\n", e->cmd, shell); _exit (OK_EXIT); } #endif /*DEBUGGING*/ #ifdef USE_SIGCHLD /* Our grandparent is watching for our parent's death by * catching SIGCHLD. Meanwhile, our parent will use wait * explicitly and so has disabled SIGCHLD. So now it's * time to reset SIGCHLD handling. */ (void) signal (SIGCHLD, SIG_DFL); #endif execle (shell, shell, "-c", e->cmd, (char *) 0, e->envp); fprintf (stderr, "execl: couldn't exec `%s'\n", shell); perror ("execl"); execle (shell, shell, "-c", e->cmd, (char *) 0, e->envp); fprintf (stderr, "execl: couldn't exec `%s'\n", shell); perror ("execl"); _exit (ERROR_EXIT); } break; default: /* parent process */ break; } children++; /* middle process, child of original cron, parent of process running * the user's command. */ Debug (DPROC, ("[%d] child continues, closing pipes\n", getpid ())); /* close the ends of the pipe that will only be referenced in the * grandchild process... */ close (stdin_pipe[READ_PIPE]); close (stdout_pipe[WRITE_PIPE]); /* * write, to the pipe connected to child's stdin, any input specified * after a % in the crontab entry. while we copy, convert any * additional %'s to newlines. when done, if some characters were * written and the last one wasn't a newline, write a newline. * * Note that if the input data won't fit into one pipe buffer (2K * or 4K on most BSD systems), and the child doesn't read its stdin, * we would block here. thus we must fork again. */ if (*input_data && fork () == 0) { register FILE *out = fdopen (stdin_pipe[WRITE_PIPE], "w"); register int need_newline = FALSE; register int escaped = FALSE; register int ch; Debug (DPROC, ("[%d] child2 sending data to grandchild\n", getpid ())); /* close the pipe we don't use, since we inherited it and * are part of its reference count now. */ close (stdout_pipe[READ_PIPE]); /* translation: * \% -> % * % -> \n * \x -> \x for all x != % */ while ((ch = *input_data++)) { if (escaped) { if (ch != '%') putc ('\\', out); } else { if (ch == '%') ch = '\n'; } if (!(escaped = (ch == '\\'))) { putc (ch, out); need_newline = (ch != '\n'); } } if (escaped) putc ('\\', out); if (need_newline) putc ('\n', out); /* close the pipe, causing an EOF condition. fclose causes * stdin_pipe[WRITE_PIPE] to be closed, too. */ fclose (out); Debug (DPROC, ("[%d] child2 done sending to grandchild\n", getpid ())); exit (0); } /* close the pipe to the grandkiddie's stdin, since its wicked uncle * ernie back there has it open and will close it when he's done. */ close (stdin_pipe[WRITE_PIPE]); children++; /* * read output from the grandchild. it's stderr has been redirected to * it's stdout, which has been redirected to our pipe. if there is any * output, we'll be mailing it to the user whose crontab this is... * when the grandchild exits, we'll get EOF. */ Debug (DPROC, ("[%d] child reading output from grandchild\n", getpid ())); /*local */ { char mailcmd[MAX_COMMAND]; register FILE *in = fdopen (stdout_pipe[READ_PIPE], "r"); register int ch = getc (in); if (ch != EOF) { register FILE *mail; register int bytes = 1; int status = 0; Debug (DPROC | DEXT, ("[%d] got data (%x:%c) from grandchild\n", getpid (), ch, ch)); /* get name of recipient. this is MAILTO if set to a * valid local username; USER otherwise. */ if (mailto) { /* MAILTO was present in the environment */ if (!*mailto) { /* ... but it's empty. set to NULL */ mailto = NULL; } } else { /* MAILTO not present, set to USER. */ mailto = usernm; } /* if we are supposed to be mailing, MAILTO will * be non-NULL. only in this case should we set * up the mail command and subjects and stuff... */ if (mailto) { register char **env; auto char hostname[MAXHOSTNAMELEN]; (void) gethostname (hostname, MAXHOSTNAMELEN); (void) snprintf (mailcmd, MAX_COMMAND, mailargs, mailprog, mailto); if (!(mail = cron_popen (mailcmd, "w", e))) { fprintf (stderr, "Unable to create process to mail job results.\n" "Mail command was: '%s'. \n" "popen() returned errno %s (%d).\n", mailcmd, strerror (errno), errno); (void) _exit (ERROR_EXIT); } fprintf (mail, "From: root (Cron Daemon)\n"); fprintf (mail, "To: %s\n", mailto); fprintf (mail, "Subject: Cron <%s@%s> %s\n", usernm, first_word (hostname, "."), e->cmd); # if defined(MAIL_DATE) fprintf (mail, "Date: %s\n", arpadate (&TargetTime)); # endif /* MAIL_DATE */ if (verbose) { for (env = e->envp; *env; env++) fprintf (mail, "X-Cron-Env: <%s>\n", *env); } fprintf (mail, "\n"); /* this was the first char from the pipe */ putc (ch, mail); } /* we have to read the input pipe no matter whether * we mail or not, but obviously we only write to * mail pipe if we ARE mailing. */ while (EOF != (ch = getc (in))) { bytes++; if (mailto) putc (ch, mail); } /* only close pipe if we opened it -- i.e., we're * mailing... */ if (mailto) { Debug (DPROC, ("[%d] closing pipe to mail\n", getpid ())); /* Note: the pclose will probably see * the termination of the grandchild * in addition to the mail process, since * it (the grandchild) is likely to exit * after closing its stdout. */ status = cron_pclose (mail); } /* if there was output and we could not mail it, * log the facts so the poor user can figure out * what's going on. */ if (mailto && status) { char buf[MAX_TEMPSTR]; snprintf (buf, MAX_TEMPSTR, "mailed %d byte%s of output but got status 0x%04x.\n" "mail command was '%s'\n", bytes, (bytes == 1) ? "" : "s", status, mailcmd); log_it (usernm, getpid (), "MAIL", buf); } } /*if data from grandchild */ Debug (DPROC, ("[%d] got EOF from grandchild\n", getpid ())); fclose (in); /* also closes stdout_pipe[READ_PIPE] */ } /* wait for children to die. */ for (; children > 0; children--) { WAIT_T waiter; pid_t pid; Debug (DPROC, ("[%d] waiting for grandchild #%d to finish\n", getpid (), children)); pid = wait (&waiter); if (pid < OK) { Debug (DPROC, ("[%d] no more grandchildren--mail written?\n", getpid ())); break; } Debug (DPROC, ("[%d] grandchild #%d finished, status=%04x", getpid (), pid, WEXITSTATUS (waiter))); if (WIFSIGNALED (waiter) && WCOREDUMP (waiter)) Debug (DPROC, (", dumped core")); Debug (DPROC, ("\n")); } }