# HG changeset patch # User Ashwini Sharma # Date 1409022071 18000 # Node ID 1d4ed4994b4cc8124d57c510bb2aecf2041e889b # Parent 487716951287b16ff6e42b45eabc90b19890c19a ipcs : display all the IPC fascilities information crond : CRON scheduler diff -r 487716951287 -r 1d4ed4994b4c toys/pending/crond.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/toys/pending/crond.c Mon Aug 25 22:01:11 2014 -0500 @@ -0,0 +1,681 @@ +/* crond.c - daemon to execute scheduled commands. + * + * Copyright 2014 Ranjan Kumar + * + * No Standard + +USE_CROND(NEWTOY(crond, "fbSl#<0=8d#<0L:c:[-bf][-LS][-ld]", TOYFLAG_USR|TOYFLAG_SBIN|TOYFLAG_NEEDROOT)) + +config CROND + bool "crond" + default n + help + usage: crond [-fbS] [-l N] [-d N] [-L LOGFILE] [-c DIR] + + A daemon to execute scheduled commands. + + -b Background (default) + -c crontab dir + -d Set log level, log to stderr + -f Foreground + -l Set log level. 0 is the most verbose, default 8 + -S Log to syslog (default) + -L Log to file +*/ + +#define FOR_crond +#include "toys.h" + +GLOBALS( + char *crontabs_dir; + char *logfile; + int loglevel_d; + int loglevel; + + time_t crontabs_dir_mtime; + uint8_t flagd; +) + +typedef struct _var { + struct _var *next, *prev; + char *name, *val; +} VAR; + +typedef struct _job { + struct _job *next, *prev; + char min[60], hour[24], dom[31], mon[12], dow[7], *cmd; + int isrunning, needstart, mailsize; + pid_t pid; +} JOB; + +typedef struct _cronfile { + struct _cronfile *next, *prev; + struct double_list *job, *var; + char *username, *mailto; + int invalid; +} CRONFILE; + +static char days[]={"sun""mon""tue""wed""thu""fri""sat"}; +static char months[]={"jan""feb""mar""apr""may""jun""jul" + "aug""sep""oct""nov""dec"}; +CRONFILE *gclist; + +#define LOG_EXIT 0 +#define LOG_LEVEL5 5 +#define LOG_LEVEL7 7 +#define LOG_LEVEL8 8 +#define LOG_LEVEL9 9 // warning +#define LOG_ERROR 20 + +static void loginfo(uint8_t loglevel, char *msg, ...) +{ + va_list s, d; + + va_start(s, msg); + va_copy(d, s); + if (loglevel >= TT.loglevel) { + int used; + char *smsg; + + if (!TT.flagd && TT.logfile) { + int fd = open(TT.logfile, O_WRONLY | O_CREAT | O_APPEND, 0666); + if (fd >=0 && fd != 2) { + dup2(fd, 2); + close(fd); + } else if (fd < 0) perror_msg("'%s", TT.logfile); + } + used = vsnprintf(NULL, 0, msg, d); + smsg = xzalloc(++used); + vsnprintf(smsg, used, msg, s); + if (TT.flagd || TT.logfile) { + fflush(NULL); + smsg[used-1] = '\n'; + writeall((loglevel > 8) ? 2 : 1, smsg, used); + } else syslog((loglevel > 8) ? LOG_ERR : LOG_INFO, "%s", smsg); + free(smsg); + } + va_end(d); + va_end(s); + if (!loglevel) exit(20); +} + +/* + * Names can also be used for the 'month' and 'day of week' fields + * (First three letters of the particular day or month). + */ +static int getindex(char *src, int size) +{ + int i; + char *field = (size == 12) ? months : days; + + // strings are not allowed for min, hour and dom fields. + if (!(size == 7 || size == 12)) return -1; + + for (i = 0; field[i]; i += 3) { + if (!strncasecmp(src, &field[i], 3)) + return (i/3); + } + return -1; +} + +// set elements of minute, hour, day of month, month and day of week arrays. +static void fillarray(char *dst, int start, int end, int skip) +{ + int sk = 1; + + if (end < 0) { + dst[start] = 1; + return; + } + if (!skip) skip = 1; + do { + if (!--sk) { + dst[start] = 1; + sk = skip; + } + } while (start++ != end); +} + +static long getval(char *num, long low, long high) +{ + long val = strtol(num, &num, 10); + + if (*num || (val < low) || (val > high)) return -1; + return val; +} + +//static int parse_and_fillarray(char *dst, int size, char *src) +static int parse_and_fillarray(char *dst, int min, int max, char *src) +{ + int start, end, skip = 0; + char *ptr = strchr(src, '/'); + + if (ptr) { + *ptr++ = 0; + if ((skip = getval(ptr, min, (min ? max: max-1))) < 0) goto ERROR; + } + + if (*src == '-' || *src == ',') goto ERROR; + if (*src == '*') { + if (*(src+1)) goto ERROR; + fillarray(dst, 0, max-1, skip); + } else { + for (;;) { + char *ctoken = strsep(&src, ","), *dtoken; + + if (!ctoken) break; + if (!*ctoken) goto ERROR; + + // Get start position. + dtoken = strsep(&ctoken, "-"); + if (isdigit(*dtoken)) { + if ((start = getval(dtoken, min, (min ? max : max-1))) < 0) goto ERROR; + start = min ? (start-1) : start; + } else if ((start = getindex(dtoken, max)) < 0) goto ERROR; + + // Get end position. + if (!ctoken) end = -1; // e.g. N1,N2,N3 + else if (*ctoken) {// e.g. N-M + if (isdigit(*ctoken)) { + if ((end = getval(ctoken, min, (min ? max : max-1))) < 0) goto ERROR; + end = min ? (end-1) : end; + } else if ((end = getindex(ctoken, max)) < 0) goto ERROR; + if (end == start) end = -1; + } else goto ERROR; // error condition 'N-' + fillarray(dst, start, end, skip); + } + } + + if (TT.flagd && (TT.loglevel <= 5)) { + for (start = 0; start < max; start++) + fprintf(stderr, "%d", (unsigned char)dst[start]); + fputc('\n', stderr); + } + return 0; +ERROR: + loginfo(LOG_LEVEL9, "parse error at %s", src); + return -1; +} + +static char *omitspace(char *line) +{ + while (*line == ' ' || *line == '\t') line++; + return line; +} + +static void parse_line(char *line, CRONFILE *cfile) +{ + int count = 0; + char *name, *val, *tokens[5] = {0,}; + VAR *v; + JOB *j; + + line = omitspace(line); + if (!*line || *line == '#') return; + + /* + * TODO: Enhancement to support 8 special strings + * @reboot -> Run once at startup. + * @yearly -> Run once a year (0 0 1 1 *). + * @annually -> Same as above. + * @monthly -> Run once a month (0 0 1 * *). + * @weekly -> Run once a week (0 0 * * 0). + * @daily -> Run once a day (0 0 * * *). + * @midnight -> same as above. + * @hourly -> Run once an hour (0 * * * *). + */ + if (*line == '@') return; + if (TT.flagd) loginfo(LOG_LEVEL5, "user:%s entry:%s", cfile->username, line); + while (count<5) { + int len = strcspn(line, " \t"); + + if (line[len]) line[len++] = '\0'; + tokens[count++] = line; + line += len; + line = omitspace(line); + if (!*line) break; + } + + switch (count) { + case 1: // form SHELL=/bin/sh + name = tokens[0]; + if ((val = strchr(name, '='))) *val++ = 0; + if (!val || !*val) return; + break; + case 2: // form SHELL =/bin/sh or SHELL= /bin/sh + name = tokens[0]; + if ((val = strchr(name, '='))) { + *val = 0; + val = tokens[1]; + } else { + if (*(tokens[1]) != '=') return; + val = tokens[1] + 1; + } + if (!*val) return; + break; + case 3: // NAME = VAL + name = tokens[0]; + val = tokens[2]; + if (*(tokens[1]) != '=') return; + break; + case 5: + // don't have any cmd to execute. + if (!*line) return; + j = xzalloc(sizeof(JOB)); + + if (parse_and_fillarray(j->min, 0, sizeof(j->min), tokens[0])) + goto STOP_PARSING; + if (parse_and_fillarray(j->hour, 0, sizeof(j->hour), tokens[1])) + goto STOP_PARSING; + if (parse_and_fillarray(j->dom, 1, sizeof(j->dom), tokens[2])) + goto STOP_PARSING; + if (parse_and_fillarray(j->mon, 1, sizeof(j->mon), tokens[3])) + goto STOP_PARSING; + if (parse_and_fillarray(j->dow, 0, sizeof(j->dow), tokens[4])) + goto STOP_PARSING; + j->cmd = xstrdup(line); + + if (TT.flagd) loginfo(LOG_LEVEL5, " command:%s", j->cmd); + dlist_add_nomalloc((struct double_list **)&cfile->job, (struct double_list *)j); + return; +STOP_PARSING: + free(j); + return; + default: return; + } + if (!strcmp(name, "MAILTO")) cfile->mailto = xstrdup(val); + else { + v = xzalloc(sizeof(VAR)); + v->name = xstrdup(name); + v->val = xstrdup(val); + dlist_add_nomalloc((struct double_list **)&cfile->var, (struct double_list *)v); + } +} + +static void free_jobs(JOB **jlist) +{ + JOB *j = dlist_pop(jlist); + free(j->cmd); + free(j); +} + +static void free_cronfile(CRONFILE **list) +{ + CRONFILE *l = dlist_pop(list); + VAR *v, *vnode = (VAR *)l->var; + + if (l->username != l->mailto) free(l->mailto); + free(l->username); + while (vnode && (v = dlist_pop(&vnode))) { + free(v->name); + free(v->val); + free(v); + } + free(l); +} + +/* + * Iterate all cronfiles to identify the completed jobs and freed them. + * If all jobs got completed for a cronfile, freed cronfile too. + */ +static void remove_completed_jobs() +{ + CRONFILE *lstart, *list = gclist; + + lstart = list; + while (list) { + int delete = 1; + JOB *jstart, *jlist = (JOB *)list->job; + + list->invalid = 1; + jstart = jlist; + while (jlist) { + jlist->isrunning = 0; + if (jlist->pid > 0) { + jlist->isrunning = 1; + delete = 0; + jlist = jlist->next; + } else { + if (jlist == jstart) { // if 1st node has to delete. + jstart = jstart->next; + free_jobs(&jlist); + continue; + } else free_jobs(&jlist); + } + if (jlist == jstart) break; + } + list->job = (struct double_list *)jlist; + + if (delete) { + if (lstart == list) { + lstart = lstart->next; + free_cronfile(&list); + continue; + } else free_cronfile(&list); + } + list = list->next; + if (lstart == list) break; + } + gclist = list; +} + +// Scan cronfiles and prepare the list of cronfiles with their jobs. +static void scan_cronfiles() +{ + DIR *dp; + struct dirent *entry; + + remove_completed_jobs(); + if (chdir(TT.crontabs_dir)) loginfo(LOG_EXIT, "chdir(%s)", TT.crontabs_dir); + if (!(dp = opendir("."))) loginfo(LOG_EXIT, "chdir(%s)", "."); + + while ((entry = readdir(dp))) { + int fd; + char *line; + CRONFILE *cfile; + + if (entry->d_name[0] == '.' && (!entry->d_name[1] || + (entry->d_name[1] == '.' && !entry->d_name[2]))) + continue; + + if (!getpwnam(entry->d_name)) { + loginfo(LOG_LEVEL7, "ignoring file '%s' (no such user)", entry->d_name); + continue; + } + if ((fd = open(entry->d_name, O_RDONLY)) < 0) continue; + + // one node for each user + cfile = xzalloc(sizeof(CRONFILE)); + cfile->username = xstrdup(entry->d_name); + + for (; (line = get_line(fd)); free(line)) + parse_line(line, cfile); + + // If there is no job for a cron, remove the VAR list. + if (!cfile->job) { + VAR *v, *vnode = (VAR *)cfile->var; + + free(cfile->username); + if (cfile->mailto) free(cfile->mailto); + + while (vnode && (v = dlist_pop(&vnode))) { + free(v->name); + free(v->val); + free(v); + } + free(cfile); + } else { + if (!cfile->mailto) cfile->mailto = cfile->username; + dlist_add_nomalloc((struct double_list **)&gclist, + (struct double_list *)cfile); + } + close(fd); + } + closedir(dp); +} + +/* + * Set env variables, if any in the cronfile. Execute given job with the given + * SHELL or Default SHELL and send an e-mail with respect to every successfully + * completed job (as per the given param 'prog'). + */ +static void do_fork(CRONFILE *cfile, JOB *job, int fd, char *prog) +{ + pid_t pid = vfork(); + + if (pid == 0) { + VAR *v, *vstart = (VAR *)cfile->var; + struct passwd *pwd = getpwnam(cfile->username); + + if (!pwd) loginfo(LOG_LEVEL9, "can't get uid for %s", cfile->username); + else { + char *file = "/bin/sh"; + + if (setenv("USER", pwd->pw_name, 1)) _exit(1); + for (v = vstart; v;) { + if (!strcmp("SHELL", v->name)) file = v->val; + if (setenv(v->name, v->val, 1)) _exit(1); + if ((v=v->next) == vstart) break; + } + if (!getenv("HOME")) { + if (setenv("HOME", pwd->pw_dir, 1)) + _exit(1); + } + xsetuser(pwd); + if (chdir(pwd->pw_dir)) loginfo(LOG_LEVEL9, "chdir(%s)", pwd->pw_dir); + if (prog) file = prog; + if (TT.flagd) loginfo(LOG_LEVEL5, "child running %s", file); + + if (fd >= 0) { + int newfd = prog ? 0 : 1; + if (fd != newfd) { + dup2(fd, newfd); + close(fd); + } + dup2(1, 2); + } + setpgrp(); + execlp(file, file, (prog ? "-ti" : "-c"), (prog ? NULL : job->cmd), (char *) NULL); + loginfo(LOG_ERROR, "can't execute '%s' for user %s", file, cfile->username); + + if (!prog) dprintf(1, "Exec failed: %s -c %s\n", file, job->cmd); + _exit(EXIT_SUCCESS); + } + } + if (pid < 0) { + loginfo(LOG_ERROR, "can't vfork"); + pid = 0; + } + if (fd >=0) close(fd); + job->pid = pid; +} + +// Send an e-mail for each successfully completed jobs. +static void sendmail(CRONFILE *cfile, JOB *job) +{ + pid_t pid = job->pid; + int mailfd; + struct stat sb; + + job->pid = 0; + if (pid <=0 || job->mailsize <=0) { + job->isrunning = 0; + job->needstart = 1; + return; + } + snprintf(toybuf, sizeof(toybuf), "/var/spool/cron/cron.%s.%d", + cfile->username, (int)pid); + + mailfd = open(toybuf, O_RDONLY); + unlink(toybuf); + if (mailfd < 0) return; + + if (fstat(mailfd, &sb) == -1 || sb.st_uid != 0 || sb.st_nlink != 0 + || sb.st_size == job->mailsize || !S_ISREG(sb.st_mode)) { + xclose(mailfd); + return; + } + job->mailsize = 0; + do_fork(cfile, job, mailfd, "sendmail"); +} + +// Count the number of jobs, which are not completed. +static int count_running_jobs() +{ + CRONFILE *cfile = gclist; + JOB *job, *jstart; + int count = 0; + + while (cfile) { + job = jstart = (JOB *)cfile->job; + while (job) { + int ret; + + if (!job->isrunning || job->pid<=0) goto NEXT_JOB; + job->isrunning = 0; + ret = waitpid(job->pid, NULL, WNOHANG); + if (ret < 0 || ret == job->pid) { + sendmail(cfile, job); + if (job->pid) count += (job->isrunning=1); + else { + job->isrunning = 0; + job->needstart = 1; + } + } + else count += (job->isrunning=1); + +NEXT_JOB: + if ((job = job->next) == jstart) break; + } + if ((cfile = cfile->next) == gclist) break; + } + return count; +} + +// Execute jobs one by one and prepare for the e-mail sending. +static void execute_jobs(void) +{ + CRONFILE *cfile = gclist; + JOB *job, *jstart; + + while (cfile) { + job = jstart = (JOB *)cfile->job; + while (job) { + if (job->needstart) { + job->needstart = 0; + if (job->pid < 0) { + int mailfd = -1; + + job->mailsize = job->pid = 0; + snprintf(toybuf, sizeof(toybuf), "/var/spool/cron/cron.%s.%d", + cfile->username, getpid()); + if ((mailfd = open(toybuf, O_CREAT|O_TRUNC|O_WRONLY|O_EXCL|O_APPEND, + 0600)) < 0) { + loginfo(LOG_ERROR, "can't create mail file %s for user %s, " + "discarding output", toybuf, cfile->username); + } else { + dprintf(mailfd, "To: %s\nSubject: cron: %s\n\n", cfile->mailto, job->cmd); + job->mailsize = lseek(mailfd, 0, SEEK_CUR); + } + do_fork(cfile, job, mailfd, NULL); + if (mailfd >= 0) { + if (job->pid <= 0) unlink(toybuf); + else { + char *mailfile = xmprintf("/var/spool/cron/cron.%s.%d", + cfile->username, (int)job->pid); + rename(toybuf, mailfile); + free(mailfile); + } + } + loginfo(LOG_LEVEL8, "USER %s pid %3d cmd %s", + cfile->username, job->pid, job->cmd); + if (job->pid < 0) job->needstart = 1; + else job->isrunning = 1; + } + } + if ((job = job->next) == jstart) break; + } + if ((cfile = cfile->next) == gclist) break; + } +} + +// Identify jobs, which needs to be started at the given time interval. +static void schedule_jobs(time_t ctime, time_t ptime) +{ + time_t tm = ptime-ptime%60; + + for (; tm <= ctime; tm += 60) { + struct tm *lt; + CRONFILE *cfile = gclist; + JOB *job, *jstart; + + if (tm <= ptime) continue; + lt = localtime(&tm); + + while (cfile) { + if (TT.flagd) loginfo(LOG_LEVEL5, "file %s:", cfile->username); + if (cfile->invalid) goto NEXT_CRONFILE; + job = jstart = (JOB *)cfile->job; + + while (job) { + if (TT.flagd) loginfo(LOG_LEVEL5, " line %s", job->cmd); + + if (job->min[lt->tm_min] && job->hour[lt->tm_hour] + && (job->dom[lt->tm_mday] || job->dow[lt->tm_wday]) + && job->mon[lt->tm_mon-1]) { + if (TT.flagd) + loginfo(LOG_LEVEL5, " job: %d %s\n", (int)job->pid, job->cmd); + if (job->pid > 0) { + loginfo(LOG_LEVEL8, "user %s: process already running: %s", + cfile->username, job->cmd); + } else if (!job->pid) { + job->pid = -1; + job->needstart = 1; + job->isrunning = 0; + } + } + if ((job = job->next) == jstart) break; + } +NEXT_CRONFILE: + if ((cfile = cfile->next) == gclist) break; + } + } +} + +void crond_main(void) +{ + time_t ctime, ptime; + int sleepfor = 60; + struct stat sb; + + TT.flagd = (toys.optflags & FLAG_d); + + // Setting default params. + if (TT.flagd) TT.loglevel = TT.loglevel_d; + if (!(toys.optflags & (FLAG_f | FLAG_b))) toys.optflags |= FLAG_b; + if (!(toys.optflags & (FLAG_S | FLAG_L))) toys.optflags |= FLAG_S; + + if ((toys.optflags & FLAG_c) + && (TT.crontabs_dir[strlen(TT.crontabs_dir)-1] != '/')) + TT.crontabs_dir = xmprintf("%s/", TT.crontabs_dir); + + if (!TT.crontabs_dir) TT.crontabs_dir = xstrdup("/var/spool/cron/crontabs/"); + if (toys.optflags & FLAG_b) daemon(0,0); + + if (!TT.flagd && !TT.logfile) + openlog(toys.which->name, LOG_CONS | LOG_PID, LOG_CRON); + + // Set default shell once. + if (setenv("SHELL", "/bin/sh", 1)) error_exit("Can't set default shell"); + xchdir(TT.crontabs_dir); + loginfo(LOG_LEVEL8, "crond started, log level %d", TT.loglevel); + + if (stat(TT.crontabs_dir, &sb)) sb.st_mtime = 0; + TT.crontabs_dir_mtime = sb.st_mtime; + scan_cronfiles(); + ctime = time(NULL); + + while (1) { + long tdiff; + + ptime = ctime; + sleep(sleepfor - (ptime%sleepfor) +1); + tdiff =(long) ((ctime = time(NULL)) - ptime); + + if (stat(TT.crontabs_dir, &sb)) sb.st_mtime = 0; + if (TT.crontabs_dir_mtime != sb.st_mtime) { + TT.crontabs_dir_mtime = sb.st_mtime; + scan_cronfiles(); + } + + if (TT.flagd) loginfo(LOG_LEVEL5, "wakeup diff=%ld\n", tdiff); + if (tdiff < -60 * 60 || tdiff > 60 * 60) + loginfo(LOG_LEVEL9, "time disparity of %ld minutes detected", tdiff / 60); + else if (tdiff > 0) { + schedule_jobs(ctime, ptime); + execute_jobs(); + if (count_running_jobs()) sleepfor = 10; + else sleepfor = 60; + } + } +} diff -r 487716951287 -r 1d4ed4994b4c toys/pending/ipcs.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/toys/pending/ipcs.c Mon Aug 25 22:01:11 2014 -0500 @@ -0,0 +1,450 @@ +/* ipcs.c - provide information on ipc facilities + * + * Copyright 2014 Sandeep Sharma + * + * see http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ipcs.html + +USE_IPCS(NEWTOY(ipcs, "acptulsqmi#", TOYFLAG_USR|TOYFLAG_BIN)) + +config IPCS + bool "ipcs" + default n + help + usage: ipcs [[-smq] -i shmid] | [[-asmq] [-tcplu]] + + -i Show specific resource + Resource specification: + -a All (default) + -m Shared memory segments + -q Message queues + -s Semaphore arrays + Output format: + -c Creator + -l Limits + -p Pid + -t Time + -u Summary +*/ + +#define FOR_ipcs +#include "toys.h" +#include +#include +#include +#include + +GLOBALS( + int id; +) + +//used many times, so good to paste it +#define flag(x) (toys.optflags & FLAG_ ## x) + +union semun { //man says declare it yourself + int val; + struct semid_ds *buf; + unsigned short *array; + struct seminfo *__buf; +}; + +static void show_msg_id(void) +{ + struct msqid_ds buf; + int ret; + + if ((ret = msgctl(TT.id, IPC_STAT, &buf)) < 0) { + perror_msg("msgctl"); + return; + } + +#define ipcperm buf.msg_perm + + printf("\nMessage Queue msqid=%d\n" + "uid=%d\tgid=%d\tcuid=%d\tcgid=%d\tmode=%#o\n", + TT.id, ipcperm.uid, ipcperm.gid, ipcperm.cuid, ipcperm.cgid,ipcperm.mode); + printf ("cbytes=%ld\tqbytes=%ld\tqnum=%ld\tlspid=%d\tlrpid=%d\n", + (long) buf.msg_cbytes, (long) buf.msg_qbytes, + (long) buf.msg_qnum, buf.msg_lspid, buf.msg_lrpid); + + printf("send_time=%-26.24s\nrcv_time=%-26.24s\nchange_time=%-26.24s\n\n", + buf.msg_stime ? ctime(&buf.msg_stime) : "Not set", + buf.msg_rtime ? ctime(&buf.msg_rtime) : "Not set", + buf.msg_ctime ? ctime(&buf.msg_ctime) : "Not set"); +#undef ipcperm +} + +static void show_sem_id(void) +{ + struct semid_ds buf; + union semun n; + int ret, i; + + n.buf = &buf; + if ((ret = semctl(TT.id, 0, IPC_STAT, n)) < 0) { + perror_msg("semctl"); + return; + } + +#define ipcperm buf.sem_perm + printf("\nSemaphore Array semid=%d\n" + "uid=%d\t gid=%d\t cuid=%d\t cgid=%d\n" + "mode=%#o, access_perms=%#o\n" + "nsems = %ld\n" + "otime = %-26.24s\n", + TT.id, + ipcperm.uid, ipcperm.gid, ipcperm.cuid, ipcperm.cgid, + ipcperm.mode, ipcperm.mode & 0777, + (long) buf.sem_nsems, + buf.sem_otime ? ctime(&buf.sem_otime) : "Not set"); + printf("ctime = %-26.24s\n" + "%-10s %-10s %-10s %-10s %-10s\n", + ctime(&buf.sem_ctime), + "semnum", "value", "ncount", "zcount", "pid"); +#undef ipcperm + + for (i = 0; i < buf.sem_nsems; i++) { + int val, nc, zc, pid; + val = semctl(TT.id, i, GETVAL, n); + nc = semctl(TT.id, i, GETNCNT, n); + zc = semctl(TT.id, i, GETZCNT, n); + pid = semctl(TT.id, i, GETPID, n); + if (val < 0 || nc < 0 || zc < 0 || pid < 0) + perror_exit("semctl"); + printf("%-10d %-10d %-10d %-10d %-10d\n", i, val, nc, zc, pid); + } + xputc('\n'); +} + +static void show_shm_id(void) +{ + struct shmid_ds buf; + int ret; + + if ((ret = shmctl(TT.id, IPC_STAT, &buf)) < 0) { + perror_msg("shmctl"); + return; + } + +#define ipcperm buf.shm_perm + + printf("\nShared memory Segment shmid=%d\n" + "uid=%d\tgid=%d\tcuid=%d\tcgid=%d\n" + "mode=%#o\taccess_perms=%#o\n" + "bytes=%ld\tlpid=%d\tcpid=%d\tnattch=%ld\n", + TT.id, + ipcperm.uid, ipcperm.gid, ipcperm.cuid, ipcperm.cgid, + ipcperm.mode, (ipcperm.mode & 0777), + (long) buf.shm_segsz, buf.shm_lpid, buf.shm_cpid, + (long) buf.shm_nattch); + printf("att_time=%-26.24s\n", + buf.shm_atime ? ctime(&buf.shm_atime) : "Not set"); + printf("det_time=%-26.24s\n", + buf.shm_dtime ? ctime(&buf.shm_dtime) : "Not set"); + printf("change_time=%-26.24s\n\n", ctime(&buf.shm_ctime)); +#undef ipcperm +} + +static void shm_array(void) +{ + struct shm_info shm_buf; + struct shminfo ipc_buf; + struct shmid_ds buf; + int max_nr, i, shmid; + struct passwd *pw; + struct group *gr; + + if ((max_nr = shmctl(0, SHM_INFO, (struct shmid_ds*)&shm_buf)) < 0) { + perror_msg("kernel not configured for shared memory"); + return; + } + + if (flag(u)) { + printf("------ Shared Memory Status --------\n"); + printf("segments allocated %d\n" + "pages allocated %ld\n" + "pages resident %ld\n" + "pages swapped %ld\n" + "Swap performance: %ld attempts\t%ld successes\n", + shm_buf.used_ids, + shm_buf.shm_tot, + shm_buf.shm_rss, + shm_buf.shm_swp, + shm_buf.swap_attempts, shm_buf.swap_successes); + return; + } + if (flag(l)) { + if ((shmctl(0, 3, (struct shmid_ds*)&ipc_buf)) < 0) return; //IPC_INFO + printf("------ Shared Memory Limits --------\n"); + printf("max number of segments = %lu\n" + "max seg size (kbytes) = %lu\n" + "max total shared memory (pages) = %lu\n" + "min seg size (bytes) = %lu\n", + (unsigned long) ipc_buf.shmmni, + (unsigned long) (ipc_buf.shmmax >> 10), + (unsigned long) ipc_buf.shmall, + (unsigned long) ipc_buf.shmmin); + return; + } + + if (flag(t)) { + printf("------ Shared Memory Attach/Detach/Change Times --------\n"); + printf("%-10s %-10s %-20s %-20s %-20s\n", + "shmid", "owner", "attached", "detached", "changed"); + } else if (flag(p)) { + printf("------ Shared Memory Creator/Last-op --------\n"); + printf("%-10s %-10s %-10s %-10s\n", + "shmid", "owner", "cpid", "lpid"); + } else if (flag(c)) { + printf("------ Shared Memory Segment Creators/Owners --------\n"); + printf("%-10s %-10s %-10s %-10s %-10s %-10s\n", + "shmid", "perms", "cuid", "cgid", "uid", "gid"); + } else { + printf("------ Shared Memory Segments --------\n"); + printf("%-10s %-10s %-10s %-10s %-10s %-10s %-12s\n", + "key", "shmid", "owner", "perms", "bytes", "nattch", + "status"); + } + + for (i = 0; i <= max_nr; i++) { + if ((shmid = shmctl(i, SHM_STAT, &buf)) < 0 ) continue; + if (flag(t)) { + if ((pw = getpwuid(buf.shm_perm.uid))) + printf("%-10d %-10.10s", shmid, pw->pw_name); + else printf("%-10d %-10.10d", shmid, buf.shm_perm.uid); + printf(" %-20.16s", buf.shm_atime + ? ctime(&buf.shm_atime) + 4 : "Not set"); + printf(" %-20.16s", buf.shm_dtime + ? ctime(&buf.shm_dtime) + 4 : "Not set"); + printf(" %-20.16s\n", buf.shm_ctime + ? ctime(&buf.shm_ctime) + 4 : "Not set"); + } else if (flag(p)) { + if ((pw = getpwuid(buf.shm_perm.uid))) + printf("%-10d %-10.10s", shmid, pw->pw_name); + else printf("%-10d %-10.10d", shmid, buf.shm_perm.uid); + printf(" %-10d %-10d\n", buf.shm_cpid, buf.shm_lpid); + } else if (flag(c)) { + printf("%-10d %-10o", shmid, buf.shm_perm.mode & 0777); + if ((pw = getpwuid(buf.shm_perm.cuid))) printf(" %-10s", pw->pw_name); + else printf(" %-10d", buf.shm_perm.cuid); + if ((gr = getgrgid(buf.shm_perm.cgid))) printf(" %-10s", gr->gr_name); + else printf(" %-10d", buf.shm_perm.cgid); + if ((pw = getpwuid(buf.shm_perm.uid))) printf(" %-10s", pw->pw_name); + else printf(" %-10d", buf.shm_perm.uid); + if ((gr = getgrgid(buf.shm_perm.gid))) printf(" %-10s\n", gr->gr_name); + else printf(" %-10d\n", buf.shm_perm.gid); + } else { + printf("0x%08x ", buf.shm_perm.__key); + if ((pw = getpwuid(buf.shm_perm.uid))) + printf("%-10d %-10.10s", shmid, pw->pw_name); + else printf("%-10d %-10.10d", shmid, buf.shm_perm.uid); + printf(" %-10o %-10lu %-10ld %-6s %-6s\n", buf.shm_perm.mode & 0777, + (unsigned long) buf.shm_segsz, + (long) buf.shm_nattch, + buf.shm_perm.mode & SHM_DEST ? "dest" : " ", + buf.shm_perm.mode & SHM_LOCKED ? "locked" : " "); + } + } +} + +static void sem_array(void) +{ + struct seminfo info_buf; + struct semid_ds buf; + union semun u; + int max_nr, i,semid; + struct passwd *pw; + struct group *gr; + + u.array = (unsigned short *)&info_buf; + if ((max_nr = semctl(0, 0, SEM_INFO, u)) < 0) { + perror_msg("kernel is not configured for semaphores"); + return; + } + + + if (flag(u)) { + printf("------ Semaphore Status --------\n"); + printf("used arrays = %d\n" + "allocated semaphores = %d\n", + info_buf.semusz, info_buf.semaem); + return; + } + if (flag(l)) { + printf("------ Semaphore Limits --------\n"); + u.array = (unsigned short *)&info_buf; + if ((semctl(0, 0, 3, u)) < 0) //IPC_INFO + return; + printf("max number of arrays = %d\n" + "max semaphores per array = %d\n" + "max semaphores system wide = %d\n" + "max ops per semop call = %d\n" + "semaphore max value = %d\n", + info_buf.semmni, + info_buf.semmsl, + info_buf.semmns, info_buf.semopm, info_buf.semvmx); + return; + } + + if (flag(t)) { + printf("------ Semaphore Operation/Change Times --------\n"); + printf("%-8s %-10s %-26.24s %-26.24s\n", + "shmid", "owner", "last-op", "last-changed"); + } else if (flag(c)) { + printf("------ Semaphore %s --------\n", "Arrays Creators/Owners"); + printf("%-10s %-10s %-10s %-10s %-10s %-10s\n", + "semid", "perms", "cuid", "cgid", "uid", "gid"); + + } else if (flag(p)){ + return; + } else { + printf("------ Semaphore %s --------\n", "Arrays"); + printf("%-10s %-10s %-10s %-10s %-10s\n", + "key", "semid", "owner", "perms", "nsems"); + } + + for (i = 0; i <= max_nr; i++) { + u.buf = &buf; + if ((semid = semctl(i, 0, SEM_STAT, u)) < 0) continue; + pw = getpwuid(buf.sem_perm.uid); + if (flag(t)) { + if (pw) printf("%-8d %-10.10s", semid, pw->pw_name); + else printf("%-8d %-10d", semid, buf.sem_perm.uid); + + printf(" %-26.24s", buf.sem_otime + ? ctime(&buf.sem_otime) : "Not set"); + printf(" %-26.24s\n", buf.sem_ctime + ? ctime(&buf.sem_ctime) : "Not set"); + } else if (flag(c)) { + printf("%-10d %-10o", semid, buf.sem_perm.mode & 0777); + if ((pw = getpwuid(buf.sem_perm.cuid))) printf(" %-10s", pw->pw_name); + else printf(" %-10d", buf.sem_perm.cuid); + if ((gr = getgrgid(buf.sem_perm.cgid))) printf(" %-10s", gr->gr_name); + else printf(" %-10d", buf.sem_perm.cgid); + if ((pw = getpwuid(buf.sem_perm.uid))) printf(" %-10s", pw->pw_name); + else printf(" %-10d", buf.sem_perm.uid); + if ((gr = getgrgid(buf.sem_perm.gid))) printf(" %-10s\n", gr->gr_name); + else printf(" %-10d\n", buf.sem_perm.gid); + } else { + printf("0x%08x ", buf.sem_perm.__key); + if (pw) printf("%-10d %-10.9s", semid, pw->pw_name); + else printf("%-10d %-9d", semid, buf.sem_perm.uid); + printf(" %-10o %-10ld\n", buf.sem_perm.mode & 0777, + (long) buf.sem_nsems); + } + } +} + +static void msg_array(void) +{ + struct msginfo info_buf; + struct msqid_ds buf; + int max_nr, i, msqid; + struct passwd *pw; + struct group *gr; + + if ((max_nr = msgctl(0, MSG_INFO, (struct msqid_ds*)&info_buf)) < 0) { + perror_msg("kernel not configured for message queue"); + return; + } + + if (flag(u)) { + printf("------ Message%s --------\n", "s: Status"); + printf("allocated queues = %d\n" + "used headers = %d\n" + "used space = %d bytes\n", + info_buf.msgpool, info_buf.msgmap, info_buf.msgtql); + return; + } + if (flag(l)) { + if ((msgctl(0, 3, (struct msqid_ds*)&info_buf)) < 0) return; //IPC_INFO + printf("------ Messages: Limits --------\n"); + printf("max queues system wide = %d\n" + "max size of message (bytes) = %d\n" + "default max size of queue (bytes) = %d\n", + info_buf.msgmni, info_buf.msgmax, info_buf.msgmnb); + return; + } + + if (flag(t)) { + printf("------ Message%s --------\n", " Queues Send/Recv/Change Times"); + printf("%-8s %-10s %-20s %-20s %-20s\n", + "msqid", "owner", "send", "recv", "change"); + } else if (flag(p)) { + printf("------ Message%s --------\n", " Queues PIDs"); + printf("%-10s %-10s %-10s %-10s\n", + "msqid", "owner", "lspid", "lrpid"); + } else if (flag(c)) { + printf("------ Message%s --------\n", " Queues: Creators/Owners"); + printf("%-10s %-10s %-10s %-10s %-10s %-10s\n", + "msqid", "perms", "cuid", "cgid", "uid", "gid"); + } else { + printf("------ Message%s --------\n", " Queues"); + printf("%-10s %-10s %-10s %-10s %-12s %-12s\n", + "key", "msqid", "owner", "perms", "used-bytes", "messages"); + } + + for (i = 0; i <= max_nr; i++) { + if ((msqid = msgctl(i, MSG_STAT, &buf)) < 0 ) continue; + pw = getpwuid(buf.msg_perm.uid); + if (flag(t)) { + if (pw) printf("%-8d %-10.10s", msqid, pw->pw_name); + else printf("%-8d %-10d", msqid, buf.msg_perm.uid); + printf(" %-20.16s", buf.msg_stime + ? ctime(&buf.msg_stime) + 4 : "Not set"); + printf(" %-20.16s", buf.msg_rtime + ? ctime(&buf.msg_rtime) + 4 : "Not set"); + printf(" %-20.16s\n", buf.msg_ctime + ? ctime(&buf.msg_ctime) + 4 : "Not set"); + } else if (flag(p)) { + if (pw) printf("%-8d %-10.10s", msqid, pw->pw_name); + else printf("%-8d %-10d", msqid, buf.msg_perm.uid); + printf(" %5d %5d\n", buf.msg_lspid, buf.msg_lrpid); + } else if (flag(c)) { + printf("%-10d %-10o", msqid, buf.msg_perm.mode & 0777); + if ((pw = getpwuid(buf.msg_perm.cuid))) printf(" %-10s", pw->pw_name); + else printf(" %-10d", buf.msg_perm.cuid); + if ((gr = getgrgid(buf.msg_perm.cgid))) printf(" %-10s", gr->gr_name); + else printf(" %-10d", buf.msg_perm.cgid); + if ((pw = getpwuid(buf.msg_perm.uid))) printf(" %-10s", pw->pw_name); + else printf(" %-10d", buf.msg_perm.uid); + if ((gr = getgrgid(buf.msg_perm.gid))) printf(" %-10s\n", gr->gr_name); + else printf(" %-10d\n", buf.msg_perm.gid); + } else { + printf("0x%08x ", buf.msg_perm.__key); + if (pw) printf("%-10d %-10.10s", msqid, pw->pw_name); + else printf("%-10d %-10d", msqid, buf.msg_perm.uid); + printf(" %-10o %-12ld %-12ld\n", buf.msg_perm.mode & 0777, + (long) buf.msg_cbytes, (long) buf.msg_qnum); + } + } +} + +void ipcs_main(void) +{ + if (flag(i)) { + if (flag(m)) show_shm_id(); + else if (flag(s)) show_sem_id(); + else if (flag(q)) show_msg_id(); + else { + toys.exithelp++; + error_exit(NULL); + } + return; + } + + if (!(flag(m) || flag(s) || flag(q)) || flag(a)) toys.optflags |= (FLAG_m|FLAG_s|FLAG_q); + + xputc('\n'); + if (flag(m)) { + shm_array(); + xputc('\n'); + } + if (flag(s)) { + sem_array(); + xputc('\n'); + } + if (flag(q)) { + msg_array(); + xputc('\n'); + } +}