static void print_proc(int index, int itv, int col) { // command char *cmd = pid_proc_cmdline(index); char *ptrcmd; if (cmd == NULL) { if (pids[index].zombie) ptrcmd = "(zombie)"; else ptrcmd = ""; } else ptrcmd = cmd; // check network namespace char *name; if (asprintf(&name, "/run/firejail/network/%d-netmap", index) == -1) errExit("asprintf"); struct stat s; if (stat(name, &s) == -1) { // the sandbox doesn't have a --net= option, don't print if (cmd) free(cmd); return; } // pid char pidstr[10]; snprintf(pidstr, 10, "%u", index); // user char *user = pid_get_user_name(pids[index].uid); char *ptruser; if (user) ptruser = user; else ptruser = ""; float rx_kbps = ((float) pids[index].rx_delta / 1000) / itv; char ptrrx[15]; sprintf(ptrrx, "%.03f", rx_kbps); float tx_kbps = ((float) pids[index].tx_delta / 1000) / itv; char ptrtx[15]; sprintf(ptrtx, "%.03f", tx_kbps); char buf[1024 + 1]; snprintf(buf, 1024, "%-5.5s %-9.9s %-10.10s %-10.10s %s", pidstr, ptruser, ptrrx, ptrtx, ptrcmd); if (col < 1024) buf[col] = '\0'; printf("%s\n", buf); if (cmd) free(cmd); if (user) free(user); }
static void print_proc(int index, int itv, int col) { // command char *cmd = pid_proc_cmdline(index); char *ptrcmd; if (cmd == NULL) { if (pids[index].zombie) ptrcmd = "(zombie)"; else ptrcmd = ""; } else ptrcmd = cmd; // if the command doesn't have a --net= option, don't print if (strstr(ptrcmd, "--net=") == NULL) { if (cmd) free(cmd); return; } // pid char pidstr[10]; snprintf(pidstr, 10, "%u", index); // user char *user = pid_get_user_name(pids[index].uid); char *ptruser; if (user) ptruser = user; else ptruser = ""; float rx_kbps = ((float) pids[index].rx_delta / 1000) / itv; char ptrrx[15]; sprintf(ptrrx, "%.03f", rx_kbps); float tx_kbps = ((float) pids[index].tx_delta / 1000) / itv; char ptrtx[15]; sprintf(ptrtx, "%.03f", tx_kbps); char buf[1024 + 1]; snprintf(buf, 1024, "%-5.5s %-9.9s %-10.10s %-10.10s %s", pidstr, ptruser, ptrrx, ptrtx, ptrcmd); if (col < 1024) buf[col] = '\0'; printf("%s\n", buf); if (cmd) free(cmd); if (user) free(user); }
static void print_elem(unsigned index, int nowrap) { // get terminal size struct winsize sz; int col = 0; if (isatty(STDIN_FILENO)) { if (!ioctl(0, TIOCGWINSZ, &sz)) col = sz.ws_col; } // indent char indent[(pids[index].level - 1) * 2 + 1]; memset(indent, ' ', sizeof(indent)); indent[(pids[index].level - 1) * 2] = '\0'; // get data uid_t uid = pids[index].uid; char *cmd = pid_proc_cmdline(index); char *user = pid_get_user_name(uid); char *allocated = user; if (user ==NULL) user = ""; if (cmd) { if (col < 4 || nowrap) printf("%s%u:%s:%s\n", indent, index, user, cmd); else { char *out; if (asprintf(&out, "%s%u:%s:%s\n", indent, index, user, cmd) == -1) errExit("asprintf"); int len = strlen(out); if (len > col) { out[col] = '\0'; out[col - 1] = '\n'; } printf("%s", out); free(out); } free(cmd); } else { if (pids[index].zombie) printf("%s%u: (zombie)\n", indent, index); else printf("%s%u:\n", indent, index); } if (allocated) free(allocated); }
// recursivity!!! static char *print_top(unsigned index, unsigned parent, unsigned *utime, unsigned *stime, unsigned itv, float *cpu, int *cnt) { char *rv = NULL; char procdir[20]; snprintf(procdir, 20, "/proc/%u", index); struct stat s; if (stat(procdir, &s) == -1) return NULL; if (pids[index].level == 1) { pgs_rss = 0; pgs_shared = 0; *utime = 0; *stime = 0; *cnt = 0; } (*cnt)++; pid_getmem(index, &pgs_rss, &pgs_shared); unsigned utmp; unsigned stmp; pid_get_cpu_time(index, &utmp, &stmp); *utime += utmp; *stime += stmp; int i; for (i = index + 1; i < MAX_PIDS; i++) { if (pids[i].parent == index) print_top(i, index, utime, stime, itv, cpu, cnt); } if (pids[index].level == 1) { // pid char pidstr[10]; snprintf(pidstr, 10, "%u", index); // command char *cmd = pid_proc_cmdline(index); char *ptrcmd; if (cmd == NULL) { if (pids[index].zombie) ptrcmd = "(zombie)"; else ptrcmd = ""; } else ptrcmd = cmd; // user char *user = pid_get_user_name(pids[index].uid); char *ptruser; if (user) ptruser = user; else ptruser = ""; char entry[60]; snprintf(entry, 60, "%s:%s:%s", pidstr, ptruser, ptrcmd); // memory int pgsz = getpagesize(); char rss[10]; snprintf(rss, 10, "%u", pgs_rss * pgsz / 1024); char shared[10]; snprintf(shared, 10, "%u", pgs_shared * pgsz / 1024); // uptime unsigned long long uptime = pid_get_start_time(index); if (clocktick == 0) clocktick = sysconf(_SC_CLK_TCK); uptime /= clocktick; uptime = sysuptime - uptime; unsigned sec = uptime % 60; uptime -= sec; uptime /= 60; unsigned min = uptime % 60; uptime -= min; uptime /= 60; unsigned hour = uptime; char uptime_str[50]; snprintf(uptime_str, 50, "%02u:%02u:%02u", hour, min, sec); // cpu itv *= clocktick; float ud = (float) (*utime - pids[index].utime) / itv * 100; float sd = (float) (*stime - pids[index].stime) / itv * 100; float cd = ud + sd; // if (cd > 100) // cd = 99.9; *cpu = cd; char cpu_str[10]; snprintf(cpu_str, 10, "%2.1f", cd); // process count char prcs_str[10]; snprintf(prcs_str, 10, "%d", *cnt); if (asprintf(&rv, "%-5.5s %-9.9s %-8.8s %-8.8s %-5.5s %-4.4s %-9.9s %s", pidstr, ptruser, rss, shared, cpu_str, prcs_str, uptime_str, ptrcmd) == -1) errExit("asprintf"); if (cmd) free(cmd); if (user) free(user); } return rv; }
// return 1 if error int name2pid(const char *name, pid_t *pid) { pid_t parent = getpid(); DIR *dir; if (!(dir = opendir("/proc"))) { // sleep 2 seconds and try again sleep(2); if (!(dir = opendir("/proc"))) { fprintf(stderr, "Error: cannot open /proc directory\n"); exit(1); } } struct dirent *entry; char *end; while ((entry = readdir(dir))) { pid_t newpid = strtol(entry->d_name, &end, 10); if (end == entry->d_name || *end) continue; if (newpid == parent) continue; // check if this is a firejail executable char *comm = pid_proc_comm(newpid); if (comm) { // remove \n char *ptr = strchr(comm, '\n'); if (ptr) *ptr = '\0'; if (strcmp(comm, "firejail")) { free(comm); continue; } free(comm); } char *cmd = pid_proc_cmdline(newpid); if (cmd) { // mark the end of the name char *ptr = strstr(cmd, "--name="); char *start = ptr; if (!ptr) { free(cmd); // extract name for /run/mnt/firejail/fslogger file char *fname; if (asprintf(&fname, "/proc/%d/root/run/firejail/mnt/fslogger", newpid) == -1) errExit("asprintf"); struct stat s; if (stat(fname, &s) == 0) { FILE *fp = fopen(fname, "r"); if (fp) { char buf[BUFLEN]; if (fgets(buf, BUFLEN, fp)) { if (strncmp(buf, "sandbox name: ", 14) == 0) { char *ptr2 = buf + 14; if (strncmp(name, ptr2, strlen(name)) == 0) { fclose(fp); *pid = newpid; closedir(dir); return 0; } } } fclose(fp); } } continue; } while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0') ptr++; *ptr = '\0'; int rv = strcmp(start + 7, name); if (rv == 0) { free(cmd); *pid = newpid; closedir(dir); return 0; } free(cmd); } } closedir(dir); return 1; }
void bandwidth_pid(pid_t pid, const char *command, const char *dev, int down, int up) { //************************ // verify sandbox //************************ char *comm = pid_proc_comm(pid); if (!comm) { fprintf(stderr, "Error: cannot find sandbox\n"); exit(1); } // remove \n and check for firejail sandbox char *ptr = strchr(comm, '\n'); if (ptr) *ptr = '\0'; if (strcmp(comm, "firejail") != 0) { fprintf(stderr, "Error: cannot find sandbox\n"); exit(1); } free(comm); // check network namespace char *cmd = pid_proc_cmdline(pid); if (!cmd || strstr(cmd, "--net") == NULL) { fprintf(stderr, "Error: the sandbox doesn't use a new network namespace\n"); exit(1); } free(cmd); //************************ // join the network namespace //************************ pid_t child; if (find_child(pid, &child) == -1) { fprintf(stderr, "Error: cannot join the network namespace\n"); exit(1); } if (join_namespace(child, "net")) { fprintf(stderr, "Error: cannot join the network namespace\n"); exit(1); } // set shm file if (strcmp(command, "set") == 0) bandwidth_shm_set(pid, dev, down, up); else if (strcmp(command, "clear") == 0) bandwidth_shm_remove(pid, dev); //************************ // build command //************************ char *devname = NULL; if (dev) { // read shm network map file char *fname; if (asprintf(&fname, "/dev/shm/firejail/%d-netmap", (int) pid) == -1) errExit("asprintf"); FILE *fp = fopen(fname, "r"); if (!fp) { fprintf(stderr, "Error: cannot read netowk map filel %s\n", fname); exit(1); } char buf[1024]; int len = strlen(dev); while (fgets(buf, 1024, fp)) { // remove '\n' char *ptr = strchr(buf, '\n'); if (ptr) *ptr = '\0'; if (*buf == '\0') break; if (strncmp(buf, dev, len) == 0 && buf[len] == ':') { devname = strdup(buf + len + 1); if (!devname) errExit("strdup"); // check device in namespace if (if_nametoindex(devname) == 0) { fprintf(stderr, "Error: cannot find network device %s\n", devname); exit(1); } break; } } free(fname); fclose(fp); } // build fshaper.sh command cmd = NULL; if (devname) { if (strcmp(command, "set") == 0) { if (asprintf(&cmd, "%s/lib/firejail/fshaper.sh --%s %s %d %d", PREFIX, command, devname, down, up) == -1) errExit("asprintf"); } else { if (asprintf(&cmd, "%s/lib/firejail/fshaper.sh --%s %s", PREFIX, command, devname) == -1) errExit("asprintf"); } } else { if (asprintf(&cmd, "%s/lib/firejail/fshaper.sh --%s", PREFIX, command) == -1) errExit("asprintf"); } assert(cmd); // wipe out environment variables environ = NULL; //************************ // build command //************************ // elevate privileges if (setreuid(0, 0)) errExit("setreuid"); if (setregid(0, 0)) errExit("setregid"); char *arg[4]; arg[0] = "/bin/bash"; arg[1] = "-c"; arg[2] = cmd; arg[3] = NULL; execvp("/bin/bash", arg); // it will never get here exit(0); }
// return 1 if error int name2pid(const char *name, pid_t *pid) { pid_t parent = getpid(); DIR *dir; if (!(dir = opendir("/proc"))) { // sleep 2 seconds and try again sleep(2); if (!(dir = opendir("/proc"))) { exechelp_logerrv("firejail", FIREJAIL_ERROR, "Error: cannot open /proc directory\n"); exit(1); } } struct dirent *entry; char *end; while ((entry = readdir(dir))) { pid_t newpid = strtol(entry->d_name, &end, 10); if (end == entry->d_name || *end) continue; if (newpid == parent) continue; // check if this is a firejail executable char *comm = pid_proc_comm(newpid); if (comm) { // remove \n char *ptr = strchr(comm, '\n'); if (ptr) *ptr = '\0'; if (strcmp(comm, "firejail")) { free(comm); continue; } free(comm); } char *cmd = pid_proc_cmdline(newpid); if (cmd) { // mark the end of the name char *ptr = strstr(cmd, "--name="); char *start = ptr; if (!ptr) { free(cmd); continue; } // set a NULL pointer on the next identified argument char *next_for_sure = strstr(ptr, " --"); if (next_for_sure) *next_for_sure = '\0'; // else we can't allow spaces in the --name argument else { while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0') ptr++; *ptr = '\0'; } int rv = strcmp(start + 7, name); if (rv == 0) { free(cmd); *pid = newpid; closedir(dir); return 0; } free(cmd); } } closedir(dir); return 1; }
// return 1 if error int name2pid(const char *name, pid_t *pid) { pid_t parent = getpid(); DIR *dir; if (!(dir = opendir("/proc"))) { // sleep 2 seconds and try again sleep(2); if (!(dir = opendir("/proc"))) { fprintf(stderr, "Error: cannot open /proc directory\n"); exit(1); } } struct dirent *entry; char *end; while ((entry = readdir(dir))) { pid_t newpid = strtol(entry->d_name, &end, 10); if (end == entry->d_name || *end) continue; if (newpid == parent) continue; // check if this is a firejail executable char *comm = pid_proc_comm(newpid); if (comm) { // remove \n char *ptr = strchr(comm, '\n'); if (ptr) *ptr = '\0'; if (strcmp(comm, "firejail")) { free(comm); continue; } free(comm); } char *cmd = pid_proc_cmdline(newpid); if (cmd) { // mark the end of the name char *ptr = strstr(cmd, "--name="); char *start = ptr; if (!ptr) { free(cmd); continue; } while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0') ptr++; *ptr = '\0'; int rv = strcmp(start + 7, name); if (rv == 0) { free(cmd); *pid = newpid; closedir(dir); return 0; } free(cmd); } } closedir(dir); return 1; }
static int procevent_monitor(const int sock, pid_t mypid) { ssize_t len; struct nlmsghdr *nlmsghdr; while (1) { char __attribute__ ((aligned(NLMSG_ALIGNTO)))buf[4096]; if ((len = recv(sock, buf, sizeof(buf), 0)) == 0) { return 0; } if (len == -1) { if (errno == EINTR) { return 0; } else { fprintf(stderr,"recv: %s\n", strerror(errno)); return -1; } } for (nlmsghdr = (struct nlmsghdr *)buf; NLMSG_OK (nlmsghdr, len); nlmsghdr = NLMSG_NEXT (nlmsghdr, len)) { struct cn_msg *cn_msg; struct proc_event *proc_ev; struct tm tm; struct timeval tv; time_t now; if ((nlmsghdr->nlmsg_type == NLMSG_ERROR) || (nlmsghdr->nlmsg_type == NLMSG_NOOP)) continue; cn_msg = NLMSG_DATA(nlmsghdr); if ((cn_msg->id.idx != CN_IDX_PROC) || (cn_msg->id.val != CN_VAL_PROC)) continue; (void)time(&now); (void)localtime_r(&now, &tm); char line[PIDS_BUFLEN]; char *lineptr = line; sprintf(lineptr, "%2.2d:%2.2d:%2.2d", tm.tm_hour, tm.tm_min, tm.tm_sec); lineptr += strlen(lineptr); proc_ev = (struct proc_event *)cn_msg->data; pid_t pid = 0; pid_t child = 0; int remove_pid = 0; switch (proc_ev->what) { case PROC_EVENT_FORK: if (proc_ev->event_data.fork.child_pid != proc_ev->event_data.fork.child_tgid) continue; // this is a thread, not a process pid = proc_ev->event_data.fork.parent_tgid; if (pids[pid].level > 0) { child = proc_ev->event_data.fork.child_tgid; child %= MAX_PIDS; pids[child].level = pids[pid].level + 1; pids[child].uid = pid_get_uid(child); } sprintf(lineptr, " fork"); break; case PROC_EVENT_EXEC: pid = proc_ev->event_data.exec.process_tgid; sprintf(lineptr, " exec"); break; case PROC_EVENT_EXIT: if (proc_ev->event_data.exit.process_pid != proc_ev->event_data.exit.process_tgid) continue; // this is a thread, not a process pid = proc_ev->event_data.exit.process_tgid; remove_pid = 1; sprintf(lineptr, " exit"); break; case PROC_EVENT_UID: pid = proc_ev->event_data.id.process_tgid; sprintf(lineptr, " uid "); break; case PROC_EVENT_GID: pid = proc_ev->event_data.id.process_tgid; sprintf(lineptr, " gid "); break; case PROC_EVENT_SID: pid = proc_ev->event_data.sid.process_tgid; sprintf(lineptr, " sid "); break; default: sprintf(lineptr, "\n"); continue; } int add_new = 0; if (pids[pid].level < 0) // not a firejail process continue; else if (pids[pid].level == 0) { // new porcess, do we have track it? if (pid_is_firejail(pid) && mypid == 0) { pids[pid].level = 1; add_new = 1; } else { pids[pid].level = -1; continue; } } lineptr += strlen(lineptr); sprintf(lineptr, " %u", pid); lineptr += strlen(lineptr); char *user = pids[pid].user; if (!user) user = pid_get_user_name(pids[pid].uid); if (user) { pids[pid].user = user; sprintf(lineptr, " (%s)", user); lineptr += strlen(lineptr); } int sandbox_closed = 0; // exit sandbox flag char *cmd = pids[pid].cmd; if (add_new) { sprintf(lineptr, " NEW SANDBOX\n"); lineptr += strlen(lineptr); } else if (proc_ev->what == PROC_EVENT_EXIT && pids[pid].level == 1) { sprintf(lineptr, " EXIT SANDBOX\n"); lineptr += strlen(lineptr); if (mypid == pid) sandbox_closed = 1; } else { if (!cmd) { cmd = pid_proc_cmdline(pid); } if (cmd == NULL) sprintf(lineptr, "\n"); else { sprintf(lineptr, " %s\n", cmd); free(cmd); } lineptr += strlen(lineptr); } (void) lineptr; // print the event printf("%s", line); fflush(0); // unflag pid for exit events if (remove_pid) { if (pids[pid].user) free(pids[pid].user); if (pids[pid].cmd) free(pids[pid].cmd); memset(&pids[pid], 0, sizeof(Process)); } // print forked child if (child) { cmd = pid_proc_cmdline(child); if (cmd) { printf("\tchild %u %s\n", child, cmd); free(cmd); } else printf("\tchild %u\n", child); } // on uid events the uid is changing if (proc_ev->what == PROC_EVENT_UID) { if (pids[pid].user) free(pids[pid].user); pids[pid].user = 0; pids[pid].uid = pid_get_uid(pid); } if (sandbox_closed) exit(0); } } return 0; }