diff options
| author | Ashwini Sharma <ak.ashwini1981@gmail.com> | 2014-08-25 22:01:11 -0500 | 
|---|---|---|
| committer | Ashwini Sharma <ak.ashwini1981@gmail.com> | 2014-08-25 22:01:11 -0500 | 
| commit | 14c8ef42870ca9623b44e369c0231766e8371bfd (patch) | |
| tree | 6de365156944a50f0dd12136aa401de350061427 | |
| parent | e996bddf88a946a8ac8338c02e14add57e3d588c (diff) | |
| download | toybox-14c8ef42870ca9623b44e369c0231766e8371bfd.tar.gz | |
ipcs : display all the IPC fascilities information
crond : CRON scheduler
| -rw-r--r-- | toys/pending/crond.c | 681 | ||||
| -rw-r--r-- | toys/pending/ipcs.c | 450 | 
2 files changed, 1131 insertions, 0 deletions
diff --git a/toys/pending/crond.c b/toys/pending/crond.c new file mode 100644 index 00000000..1b5e4194 --- /dev/null +++ b/toys/pending/crond.c @@ -0,0 +1,681 @@ +/* crond.c - daemon to execute scheduled commands. + * + * Copyright 2014 Ranjan Kumar <ranjankumar.bth@gmail.com> + * + * 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 --git a/toys/pending/ipcs.c b/toys/pending/ipcs.c new file mode 100644 index 00000000..d974a519 --- /dev/null +++ b/toys/pending/ipcs.c @@ -0,0 +1,450 @@ +/* ipcs.c - provide information on ipc facilities + * + * Copyright 2014 Sandeep Sharma <sandeep.jack2756@gmail.com> + * + * 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 <sys/ipc.h> +#include <sys/shm.h> +#include <sys/sem.h> +#include <sys/msg.h> + +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'); +  } +}  | 
