Mercurial > hg > toybox
view toys/pending/bootchartd.c @ 1572:da1bf31ed322 draft
Tweak the "ignoring return value" fortify workaround for readlinkat.
We zero the buffer and if the link read fails that's left alone, so
it's ok for the symlink not to be there. Unfortunately, typecasting the
return value to (void) doesn't shut up gcc, and having an if(); with the
semicolon on the same line doesn't shut up llvm. (The semicolon on a new
line would, but C does not have significant whitespace and I'm not going
to humor llvm if it plans to start.)
So far, empty curly brackets consistently get the warning to shut up.
author | Rob Landley <rob@landley.net> |
---|---|
date | Mon, 24 Nov 2014 17:23:23 -0600 |
parents | c0c91437138b |
children | 31dded5e0e09 |
line wrap: on
line source
/* bootchartd.c - bootchartd is commonly used to profile the boot process. * * Copyright 2014 Bilal Qureshi <bilal.jmi@gmail.com> * Copyright 2014 Kyungwan Han <asura321@gmail.com> * * No Standard USE_BOOTCHARTD(NEWTOY(bootchartd, 0, TOYFLAG_STAYROOT|TOYFLAG_USR|TOYFLAG_BIN)) config BOOTCHARTD bool "bootchartd" default n help usage: bootchartd {start [PROG ARGS]}|stop|init Create /var/log/bootlog.tgz with boot chart data start: start background logging; with PROG, run PROG, then kill logging with USR1 stop: send USR1 to all bootchartd processes init: start background logging; stop when getty/xdm is seen (for init scripts) Under PID 1: as init, then exec $bootchart_init, /init, /sbin/init */ #define FOR_bootchartd #include "toys.h" GLOBALS( char buf[32]; long smpl_period_usec; int proc_accounting; int is_login; void *head; ) struct pid_list { struct pid_list *next, *prev; int pid; }; static int push_pids_in_list(pid_t pid, char *name) { struct pid_list *new = xzalloc(sizeof(struct pid_list)); new->pid = pid; dlist_add_nomalloc((void *)&TT.head, (void *)new); return 0; } static void dump_data_in_file(char *fname, int wfd) { int rfd = open(fname, O_RDONLY); if (rfd != -1) { xwrite(wfd, TT.buf, strlen(TT.buf)); xsendfile(rfd, wfd); close(rfd); xwrite(wfd, "\n", 1); } } static int dump_proc_data(FILE *fp) { struct dirent *pid_dir; int login_flag = 0; pid_t pid; DIR *proc_dir = opendir("/proc"); fputs(TT.buf, fp); while ((pid_dir = readdir(proc_dir))) { char filename[64]; int fd; if (!isdigit(pid_dir->d_name[0])) continue; sscanf(pid_dir->d_name, "%d", &pid); sprintf(filename, "/proc/%d/stat", pid); if ((fd = open(filename, O_RDONLY)) != -1 ) { char *ptr; ssize_t len; if ((len = readall(fd, toybuf, sizeof(toybuf)-1)) < 0) { xclose(fd); continue; } toybuf[len] = '\0'; close(fd); fputs(toybuf, fp); if (!TT.is_login) continue; if ((ptr = strchr(toybuf, '('))) { char *tmp = strchr(++ptr, ')'); if (tmp) *tmp = '\0'; } // Checks for gdm, kdm or getty if (((ptr[0] == 'g' || ptr[0] == 'k' || ptr[0] == 'x') && ptr[1] == 'd' && ptr[2] == 'm') || strstr(ptr, "getty")) login_flag = 1; } } closedir(proc_dir); fputc('\n', fp); return login_flag; } static int parse_config_file(char *fname) { size_t len = 0; char *line = NULL; FILE *fp = fopen(fname, "r"); if (!fp) return 0; for (;getline(&line, &len, fp) != -1; line = NULL) { char *ptr = line; while (*ptr == ' ' || *ptr == '\t') ptr++; if (!*ptr || *ptr == '#' || *ptr == '\n') continue; if (!strncmp(ptr, "SAMPLE_PERIOD", strlen("SAMPLE_PERIOD"))) { double smpl_val; if ((ptr = strchr(ptr, '='))) ptr += 1; else continue; sscanf(ptr, "%lf", &smpl_val); TT.smpl_period_usec = smpl_val * 1000000; if (TT.smpl_period_usec <= 0) TT.smpl_period_usec = 1; } if (!strncmp(ptr, "PROCESS_ACCOUNTING", strlen("PROCESS_ACCOUNTING"))) { if ((ptr = strchr(ptr, '='))) ptr += 1; else continue; sscanf(ptr, "%s", toybuf); // string will come with double quotes. if (!(strncmp(toybuf+1, "on", strlen("on"))) || !(strncmp(toybuf+1, "yes", strlen("yes")))) TT.proc_accounting = 1; } free(line); } fclose(fp); return 1; } static char *create_tmp_dir() { char *dir_list[] = {"/tmp", "/mnt", "/boot", "/proc"}, **target = dir_list; char *dir, dir_path[] = "/tmp/bootchart.XXXXXX"; if ((dir = mkdtemp(dir_path))) { xchdir((dir = xstrdup(dir))); return dir; } while (mount("none", *target, "tmpfs", (1<<15), "size=16m")) //MS_SILENT if (!++target) perror_exit("can't mount tmpfs"); xchdir(*target); if (umount2(*target, MNT_DETACH)) perror_exit("Can't unmount tmpfs"); return *target; } static void start_logging() { int proc_stat_fd = xcreate("proc_stat.log", O_WRONLY | O_CREAT | O_TRUNC, 0644); int proc_diskstats_fd = xcreate("proc_diskstats.log", O_WRONLY | O_CREAT | O_TRUNC, 0644); FILE *proc_ps_fp = xfopen("proc_ps.log", "w"); long tcnt = 60 * 1000 * 1000 / TT.smpl_period_usec; if (tcnt <= 0) tcnt = 1; if (TT.proc_accounting) { int kp_fd = xcreate("kernel_procs_acct", O_WRONLY | O_CREAT | O_TRUNC,0666); xclose(kp_fd); acct("kernel_procs_acct"); } memset(TT.buf, 0, sizeof(TT.buf)); while (--tcnt && !toys.signal) { int i = 0, j = 0, fd = open("/proc/uptime", O_RDONLY); if (fd < 0) goto wait_usec; char *line = get_line(fd); if (!line) goto wait_usec; while (line[i] != ' ') { if (line[i] == '.') { i++; continue; } TT.buf[j++] = line[i++]; } TT.buf[j++] = '\n'; TT.buf[j] = '\0'; free(line); close(fd); dump_data_in_file("/proc/stat", proc_stat_fd); dump_data_in_file("/proc/diskstats", proc_diskstats_fd); // stop proc dumping in 2 secs if getty or gdm, kdm, xdm found if (dump_proc_data(proc_ps_fp)) if (tcnt > 2 * 1000 * 1000 / TT.smpl_period_usec) tcnt = 2 * 1000 * 1000 / TT.smpl_period_usec; fflush(NULL); wait_usec: usleep(TT.smpl_period_usec); } xclose(proc_stat_fd); xclose(proc_diskstats_fd); fclose(proc_ps_fp); } static void stop_logging(char *tmp_dir, char *prog) { char host_name[32]; int kcmd_line_fd; time_t t; struct tm st; struct utsname uts; FILE *hdr_fp = xfopen("header", "w"); if (TT.proc_accounting) acct(NULL); if (prog) fprintf(hdr_fp, "profile.process = %s\n", prog); gethostname(host_name, sizeof(host_name)); time(&t); localtime_r(&t, &st); memset(toybuf, 0, sizeof(toybuf)); strftime(toybuf, sizeof(toybuf), "%a %b %e %H:%M:%S %Z %Y", &st); fprintf(hdr_fp, "version = TBX_BCHARTD_VER 1.0.0\n"); fprintf(hdr_fp, "title = Boot chart for %s (%s)\n", host_name, toybuf); if (uname(&uts) < 0) perror_exit("uname"); fprintf(hdr_fp, "system.uname = %s %s %s %s\n", uts.sysname, uts.release, uts.version, uts.machine); memset(toybuf, 0, sizeof(toybuf)); if ((kcmd_line_fd = open("/proc/cmdline", O_RDONLY)) != -1) { ssize_t len; if ((len = readall(kcmd_line_fd, toybuf, sizeof(toybuf)-1)) > 0) { toybuf[len] = 0; while (--len >= 0 && !toybuf[len]) continue; for (; len > 0; len--) if (toybuf[len] < ' ') toybuf[len] = ' '; } else *toybuf = 0; } fprintf(hdr_fp, "system.kernel.options = %s", toybuf); close(kcmd_line_fd); fclose(hdr_fp); memset(toybuf, 0, sizeof(toybuf)); snprintf(toybuf, sizeof(toybuf), "tar -zcf /var/log/bootlog.tgz header %s *.log", TT.proc_accounting ? "kernel_procs_acct" : ""); system(toybuf); if (tmp_dir) { unlink("header"); unlink("proc_stat.log"); unlink("proc_diskstats.log"); unlink("proc_ps.log"); if (TT.proc_accounting) unlink("kernel_procs_acct"); rmdir(tmp_dir); } } void bootchartd_main() { pid_t lgr_pid, self_pid = getpid(); int bchartd_opt = 0; // 0=PID1, 1=start, 2=stop, 3=init TT.smpl_period_usec = 200 * 1000; TT.is_login = (self_pid == 1); if (*toys.optargs) { if (!strcmp("start", *toys.optargs)) bchartd_opt = 1; else if (!strcmp("stop", *toys.optargs)) bchartd_opt = 2; else if (!strcmp("init", *toys.optargs)) bchartd_opt = 3; else error_exit("Unknown option '%s'", *toys.optargs); if (bchartd_opt == 2) { struct pid_list *temp; char *process_name[] = {"bootchartd", NULL}; names_to_pid(process_name, push_pids_in_list); temp = TT.head; if (temp) temp->prev->next = 0; for (; temp; temp = temp->next) if (temp->pid != self_pid) kill(temp->pid, SIGUSR1); llist_traverse(TT.head, free); return; } } else if (!TT.is_login) error_exit("not PID 1"); // Execute the code below for start or init or PID1 if (!parse_config_file("bootchartd.conf")) parse_config_file("/etc/bootchartd.conf"); memset(toybuf, 0, sizeof(toybuf)); if (!(lgr_pid = xfork())) { char *tmp_dir = create_tmp_dir(); sigatexit(generic_signal); raise(SIGSTOP); if (!bchartd_opt && !getenv("PATH")) putenv("PATH=/sbin:/usr/sbin:/bin:/usr/bin"); start_logging(); stop_logging(tmp_dir, bchartd_opt == 1 ? toys.optargs[1] : NULL); free(tmp_dir); return; } waitpid(lgr_pid, NULL, WUNTRACED); kill(lgr_pid, SIGCONT); if (!bchartd_opt) { char *pbchart_init = getenv("bootchart_init"); if (pbchart_init) execl(pbchart_init, pbchart_init, NULL); execl("/init", "init", (void *)0); execl("/sbin/init", "init", (void *)0); } if (bchartd_opt == 1 && toys.optargs[1]) { pid_t prog_pid; if (!(prog_pid = xfork())) xexec_optargs(1); waitpid(prog_pid, NULL, 0); kill(lgr_pid, SIGUSR1); } }