"Fossies" - the Fresh Open Source Software Archive

Member "userinfo-2.5/src/modules/login.c" (15 Jan 2015, 18760 Bytes) of package /linux/privat/userinfo-2.5.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "login.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.4_vs_2.5.

    1 /*
    2     Copyright (C) 2001-2015 Ben Kibbey <bjk@luxsci.net>
    3 
    4     This program is free software; you can redistribute it and/or modify
    5     it under the terms of the GNU General Public License as published by
    6     the Free Software Foundation; either version 2 of the License, or
    7     (at your option) any later version.
    8 
    9     This program is distributed in the hope that it will be useful,
   10     but WITHOUT ANY WARRANTY; without even the implied warranty of
   11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12     GNU General Public License for more details.
   13 
   14     You should have received a copy of the GNU General Public License
   15     along with this program; if not, write to the Free Software
   16     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02110-1301  USA
   17 */
   18 #ifdef HAVE_CONFIG_H
   19 #include <config.h>
   20 #endif
   21 
   22 #include <stdio.h>
   23 #include <unistd.h>
   24 #include <stdlib.h>
   25 #include <sys/types.h>
   26 #include <sys/stat.h>
   27 #include <errno.h>
   28 #include <time.h>
   29 #include <pwd.h>
   30 #include <ctype.h>
   31 
   32 #ifdef HAVE_LIMITS_H
   33 #include <limits.h>
   34 #ifndef LINE_MAX
   35 #ifdef _POSIX2_LINE_MAX
   36 #define LINE_MAX _POSIX2_LINE_MAX
   37 #else
   38 #define LINE_MAX 2048
   39 #endif
   40 #endif
   41 #endif
   42 
   43 #ifndef HAVE_STRSEP
   44 #include "../strsep.c"
   45 #endif
   46 
   47 #ifndef HAVE_ERR_H
   48 #include "../err.c"
   49 #endif
   50 
   51 #include "login.h"
   52 
   53 #define LOGIN_OPTION_ORDER  "pdimyhtl"
   54 #define LOGIN_OPTION_STRING "Lpdimyhtl:"
   55 
   56 static char options[9];     /* NULL terminated. */
   57 static char *last_options;
   58 static char **strings;
   59 static time_t now;
   60 static int login_count;
   61 #if (!defined(__FreeBSD_version) || __FreeBSD_version < 900000)
   62 static int lastlogfd;
   63 #endif
   64 
   65 void add_string(char ***, const char *);
   66 char *stamp(time_t, const char *);
   67 char *safe_strncat(char *, const char *, size_t);
   68 
   69 void ui_module_init(int *chainable)
   70 {
   71     *chainable = 0;
   72     time(&now);
   73 }
   74 
   75 void ui_module_exit()
   76 {
   77 #ifdef HAVE_PROCFS
   78     if (procdir)
   79     closedir(procdir);
   80 #endif
   81 
   82 #ifdef HAVE_KVM_H
   83     if (kd)
   84     kvm_close(kd);
   85 #endif
   86 
   87 #if (!defined(__FreeBSD_version) || __FreeBSD_version < 900000)
   88     if (lastlogfd)
   89     close(lastlogfd);
   90 #endif
   91 }
   92 
   93 static void free_logins(UTMP **u)
   94 {
   95     if (!u)
   96         return;
   97 
   98     if (login_count) {
   99     UTMP **up;
  100 
  101     for (up = u; *up; up++)
  102         free(*up);
  103 
  104     free(u);
  105     }
  106 }
  107 
  108 #ifndef HAVE_UTMPX_H
  109 /* This is for *BSD (login process id). */
  110 #ifdef BSD_KVM
  111 static char *get_pid(uid_t uid, int multi)
  112 {
  113     static int firstrun;
  114     static char line[LINE_MAX];
  115     int cnt, i;
  116     char errbuf[LINE_MAX];
  117     struct kinfo_proc *kp;
  118 
  119     line[0] = '\0';
  120 
  121     if (!kd && firstrun)
  122     return "!";
  123 
  124     if (!kd) {
  125     firstrun = 1;
  126 
  127 #if defined(__NetBSD__) || defined (__OpenBSD__)
  128     if ((kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf)) == NULL) {
  129 #else
  130     if ((kd = kvm_openfiles(_PATH_DEVNULL, _PATH_DEVNULL, _PATH_DEVNULL,
  131                 O_RDONLY, errbuf)) == NULL) {
  132 #endif
  133         warnx("%s", errbuf);
  134         return "!";
  135     }
  136     }
  137 
  138 #ifdef __OpenBSD__
  139     if ((kp = kvm_getprocs(kd, KERN_PROC_UID, uid, sizeof(struct kinfo_proc),
  140      &cnt)) == NULL) {
  141     warnx("kvm_getprocs(): %s", kvm_geterr(kd));
  142     return "!";
  143     }
  144 #else
  145     if ((kp = kvm_getprocs(kd, KERN_PROC_UID, uid, &cnt)) == NULL) {
  146     warnx("kvm_getprocs(): %s", kvm_geterr(kd));
  147     return "!";
  148     }
  149 #endif
  150 
  151     for (i = 0; i < cnt; i++) {
  152     char buf[32];
  153     pid_t pid = 0;
  154 
  155 #ifdef __OpenBSD__
  156     if ((kp[i].p_eflag & EPROC_SLEADER) && kp[i].p_tdev != -1)
  157         pid = kp[i].p_pid;
  158 #else
  159 #if __FreeBSD_version < 500000
  160     if (kp[i].kp_eproc.e_flag & EPROC_SLEADER && kp[i].kp_eproc.e_tdev
  161         != -1) {
  162         pid = kp[i].kp_eproc.e_ppid;
  163         /*
  164          * pid = kp[i].kp_proc.p_pid;
  165          */
  166     }
  167 #else
  168     if (kp[i].ki_kiflag & KI_SLEADER && kp[i].ki_tdev != -1) {
  169         pid = kp[i].ki_pid;
  170     }
  171 #endif
  172 #endif
  173     if (!pid || pid == 1)
  174         continue;
  175 
  176         snprintf(buf, sizeof(buf), "%i%c", pid, multi);
  177     safe_strncat(line, buf, sizeof(line));
  178     }
  179 
  180     if (line[0] == '\0')
  181     return "!";
  182 
  183     line[strlen(line) - 1] = '\0';
  184     return line;
  185 }
  186 
  187 /* This is for Linux and Solaris. */
  188 #elif defined(HAVE_PROCFS)
  189 #include <sys/types.h>
  190 #include <sys/stat.h>
  191 
  192 #ifdef HAVE_DIRENT_H
  193 #include <dirent.h>
  194 #endif
  195 
  196 #ifdef __sun__
  197 #include <unistd.h>
  198 #include <procfs.h>
  199 #endif
  200 
  201 static char *get_pid(uid_t uid, int multi)
  202 {
  203     static int firstrun;
  204     struct dirent *ent;
  205     struct stat st;
  206     static char line[LINE_MAX];
  207     pid_t *pids = 0, *tpids;
  208     int pid_index = 0;
  209 
  210 #ifdef __sun__
  211     int fd;
  212     struct pstatus pstat;
  213 #else
  214     FILE *fp;
  215 #endif
  216 
  217     line[0] = '\0';
  218 
  219     if (!procdir && firstrun)
  220     return "!";
  221 
  222     if (!procdir) {
  223     firstrun = 1;
  224 
  225     if ((procdir = opendir("/proc")) == NULL) {
  226         warn("%s", "/proc");
  227         return "!";
  228     }
  229     }
  230 
  231     rewinddir(procdir);
  232 
  233 again:
  234     while ((ent = readdir(procdir)) != NULL) {
  235     pid_t pid = -1;
  236     char filename[FILENAME_MAX];
  237     char buf[LINE_MAX];
  238     int i;
  239 
  240 #ifndef __sun__
  241     char *t;
  242 #endif
  243 
  244     if (!isdigit((unsigned char) *ent->d_name))
  245         continue;
  246 
  247 #ifdef __linux__
  248     snprintf(filename, sizeof(filename), "/proc/%s/stat", ent->d_name);
  249 #else
  250     snprintf(filename, sizeof(filename), "/proc/%s/status", ent->d_name);
  251 #endif
  252 
  253     if (stat(filename, &st) == -1)
  254         continue;
  255 
  256     /*
  257      * The current user owns this file (process id).
  258      */
  259     if (st.st_uid == uid) {
  260 #ifdef __sun__
  261         if ((fd = open(filename, O_RDONLY)) == -1)
  262         continue;
  263 
  264         if (pread(fd, &pstat, sizeof(struct pstatus), 0) !=
  265         sizeof(struct pstatus)) {
  266         close(fd);
  267         continue;
  268         }
  269 
  270         pid = pstat.pr_ppid;
  271         close(fd);
  272 #else
  273         if ((fp = fopen(filename, "r")) == NULL)
  274         continue;
  275 
  276         if ((t = fgets(buf, sizeof(buf), fp)) == NULL) {
  277         fclose(fp);
  278         continue;
  279         }
  280 
  281 #ifdef __linux__
  282         if ((i = sscanf(buf, "%*i %*s %*c %*i %*i %i", &pid)) < 1) {
  283 #endif
  284 /*
  285 #else
  286         if ((i = sscanf(buf, "%*s %*i %li", &ppid)) < 1) {
  287 #endif
  288 */
  289         fclose(fp);
  290         continue;
  291         }
  292 
  293         fclose(fp);
  294 #endif
  295 
  296         /*
  297          * Skip duplicate pids.
  298          */
  299         for (i = 0; i < pid_index; i++) {
  300         if (pids[i] == pid)
  301             goto again;
  302         }
  303 
  304         snprintf(buf, sizeof(buf), "%li%c", (unsigned long) pid, multi);
  305         safe_strncat(line, buf, sizeof(line));
  306 
  307         if ((tpids =
  308          realloc(pids, (pid_index + 2) * sizeof(pid_t *))) == NULL) {
  309         warn("realloc()");
  310         continue;
  311         }
  312 
  313         pids = tpids;
  314         pids[pid_index++] = pid;
  315     }
  316     }
  317 
  318     if (pid_index)
  319     free(pids);
  320 
  321     if (line[0] == '\0')
  322     return "!";
  323 
  324     line[strlen(line) - 1] = '\0';
  325     return line;
  326 }
  327 #else
  328 /* Unsupported OS. */
  329 static char *get_pid(uid_t uid, int multi)
  330 {
  331     return "!";
  332 }
  333 #endif
  334 #endif
  335 
  336 /* Break up the last login string into sections and add the sections to the
  337  * output string array if needed. */
  338 static void last_strings(char *str)
  339 {
  340     int i = 0;
  341     char *buf;
  342     const char *line, *host, *when;
  343 
  344     line = host = when = (str) ? "-" : "!";
  345 
  346     while ((buf = strsep(&str, ",")) != NULL) {
  347     if (!buf[0])
  348         continue;
  349 
  350     switch (i++) {
  351         case 0:
  352         line = buf;
  353         break;
  354         case 1:
  355         host = buf;
  356         break;
  357         case 2:
  358         when = buf;
  359         break;
  360         default:
  361         break;
  362     }
  363     }
  364 
  365     for (i = 0; i < strlen(last_options); i++) {
  366     switch (last_options[i]) {
  367         case 'y':
  368         add_string(&strings, line);
  369         break;
  370         case 'h':
  371         add_string(&strings, host);
  372         break;
  373         case 't':
  374         add_string(&strings, when);
  375         break;
  376         case 'a':
  377         add_string(&strings, line);
  378         add_string(&strings, host);
  379         add_string(&strings, when);
  380         default:
  381         break;
  382     }
  383     }
  384 }
  385 
  386 /* Get the lastlog structure from the lastlog file. */
  387 #ifdef HAVE_GETLASTLOGX
  388 static char *lastlogin(const struct passwd *pw, char *tf)
  389 {
  390     struct lastlogx *last =  getlastlogx(_PATH_LASTLOGX, pw->pw_uid, NULL);
  391     static char buf[LINE_MAX];
  392 
  393     if (!last)
  394     return NULL;
  395 
  396     snprintf(buf, sizeof(buf), "%s,%s,%s",
  397     !last->ll_line[0] ? "!" : last->ll_line,
  398     (!last->ll_host[0] || !isalnum(last->ll_host[0])) ?  "-" : last->ll_host,
  399     stamp(last->ll_tv.tv_sec, tf));
  400     return buf;
  401 }
  402 #else
  403 #if __FreeBSD_version >= 900000
  404 static char *lastlogin(const struct passwd *pw, char *tf)
  405 {
  406     struct utmpx *last;
  407     static char buf[LINE_MAX];
  408 
  409     if (setutxdb(UTXDB_LASTLOGIN, NULL) == -1) {
  410     warn("lastlog");
  411     return NULL;
  412     }
  413 
  414     last = getutxuser(pw->pw_name);
  415 
  416     if (!last)
  417     return NULL;
  418 
  419     snprintf(buf, sizeof(buf), "%s,%s,%s",
  420     !last->ut_line[0] ? "!" : last->ut_line,
  421     (!last->ut_host[0] || !isalnum(last->ut_host[0])) ?  "-" : last->ut_host,
  422     stamp(last->ut_tv.tv_sec, tf));
  423 
  424     return buf;
  425 }
  426 #else
  427 static char *lastlogin(const struct passwd *pw, char *tf)
  428 {
  429     int count;
  430     long offset;
  431     static char buf[LINE_MAX];
  432     struct lastlog last;
  433 
  434     if (lastlogfd < 0)
  435     return NULL;
  436 
  437     if (!lastlogfd) {
  438     if ((lastlogfd = open(_PATH_LASTLOG, O_RDONLY)) == -1) {
  439         warn("%s", _PATH_LASTLOG);
  440         return NULL;
  441     }
  442     }
  443 
  444     offset = (long) pw->pw_uid * sizeof(struct lastlog);
  445 
  446     if (lseek(lastlogfd, offset, SEEK_SET) == -1) {
  447     warn("%s", _PATH_LASTLOG);
  448     return NULL;
  449     }
  450 
  451     if ((count = read(lastlogfd, &last, sizeof(struct lastlog))) !=
  452     sizeof(struct lastlog)) {
  453     if (count == -1)
  454         warn("%s", _PATH_LASTLOG);
  455 
  456     return NULL;
  457     }
  458 
  459 #ifdef __NetBSD__
  460 #ifdef HAVE_UTMPX_H
  461     last.ll_host[UTX_HOSTSIZE-1] = '\0';
  462     last.ll_line[UTX_LINESIZE-1] = '\0';
  463 #else
  464     last.ll_host[UT_HOSTSIZE-1] = '\0';
  465     last.ll_line[UT_LINESIZE-1] = '\0';
  466 #endif
  467 #else
  468     last.ll_host[UT_HOSTSIZE-1] = '\0';
  469     last.ll_line[UT_LINESIZE-1] = '\0';
  470 #endif
  471 
  472     snprintf(buf, sizeof(buf), "%s,%s,%s",
  473     !last.ll_line[0] ? "!" : last.ll_line,
  474     (!last.ll_host[0] || !isalnum(last.ll_host[0])) ?
  475         !isdigit(last.ll_line[3]) ? "!" : "-" : last.ll_host,
  476     !last.ll_time ? "!" : stamp(last.ll_time, tf));
  477     return buf;
  478 }
  479 #endif
  480 #endif
  481 
  482 /* This will return an array of utmp structures if a user is logged in, NULL
  483  * otherwise. We'll try to keep the utmp file descriptor open if possible to
  484  * speed things up a bit. */
  485 static UTMP **get_utmp(const char *user)
  486 {
  487     UTMP **logins = NULL;
  488 #if defined HAVE_UTMPX_H && defined (HAVE_SETUTXENT)
  489     UTMP *u;
  490 #else
  491     UTMP u;
  492     int count;
  493     static int fd;
  494 
  495     if (fd < 0)
  496     return NULL;
  497 
  498     if (!fd) {
  499     if ((fd = open(_PATH_UTMP, O_RDONLY)) == -1) {
  500         warn("%s", _PATH_UTMP);
  501         return NULL;
  502     }
  503     }
  504 #endif
  505 
  506     login_count = 0;
  507 
  508 #if defined HAVE_UTMPX_H && defined (HAVE_SETUTXENT)
  509     setutxent();
  510 
  511     while ((u = getutxent()) != NULL) {
  512     if (!strcmp(u->ut_user, user) && u->ut_type != DEAD_PROCESS) {
  513 #else
  514     lseek(fd, 0, SEEK_SET);
  515 
  516     while ((count = read(fd, &u, sizeof(UTMP))) == sizeof(UTMP)) {
  517     if (strcmp(u.ut_name, user) == 0) {
  518 #endif
  519         UTMP **tmp;
  520 
  521         if ((tmp = realloc(logins,
  522                   (login_count + 2) * sizeof(UTMP *))) ==
  523         NULL) {
  524         warn("realloc()");
  525         free_logins(logins);
  526         return NULL;
  527         }
  528 
  529         logins = tmp;
  530 
  531         if ((logins[login_count] = malloc(sizeof(UTMP))) == NULL) {
  532         warn("malloc()");
  533         free_logins(logins);
  534         return NULL;
  535         }
  536 
  537 #if defined HAVE_UTMPX_H && defined (HAVE_SETUTXENT)
  538 #ifdef __NetBSD__
  539         memcpy(logins[login_count]->ut_name, u->ut_name, UTX_NAMESIZE);
  540         logins[login_count]->ut_name[UTX_NAMESIZE-1] = 0;
  541         memcpy(logins[login_count]->ut_line, u->ut_line, UTX_LINESIZE);
  542         logins[login_count]->ut_line[UTX_LINESIZE-1] = 0;
  543         memcpy(logins[login_count]->ut_host, u->ut_host, UTX_HOSTSIZE);
  544         logins[login_count]->ut_host[UTX_HOSTSIZE-1] = 0;
  545         logins[login_count]->ut_pid = u->ut_pid;
  546 #else
  547         memcpy(logins[login_count]->ut_user, u->ut_user, UT_NAMESIZE);
  548         logins[login_count]->ut_user[UT_NAMESIZE-1] = 0;
  549         memcpy(logins[login_count]->ut_line, u->ut_line, UT_LINESIZE);
  550         logins[login_count]->ut_line[UT_LINESIZE-1] = 0;
  551         memcpy(logins[login_count]->ut_host, u->ut_host, UT_HOSTSIZE);
  552         logins[login_count]->ut_host[UT_HOSTSIZE-1] = 0;
  553         logins[login_count]->ut_tv.tv_sec = u->ut_tv.tv_sec;
  554         logins[login_count]->ut_pid = u->ut_pid;
  555 #endif
  556 #else
  557         memcpy(logins[login_count]->ut_name, u.ut_name, UT_NAMESIZE);
  558         logins[login_count]->ut_name[UT_NAMESIZE-1] = 0;
  559         memcpy(logins[login_count]->ut_line, u.ut_line, UT_LINESIZE);
  560         logins[login_count]->ut_line[UT_LINESIZE-1] = 0;
  561         memcpy(logins[login_count]->ut_host, u.ut_host, UT_HOSTSIZE);
  562         logins[login_count]->ut_host[UT_HOSTSIZE-1] = 0;
  563         logins[login_count]->ut_time = u.ut_time;
  564 #endif
  565         logins[++login_count] = NULL;
  566     }
  567     }
  568 
  569     return logins;
  570 }
  571 
  572 /* The 'mesg' status of the logged in user. */
  573 static char *msgstat(UTMP **u, int multi)
  574 {
  575     static char line[LINE_MAX];
  576     int i;
  577 
  578     line[0] = '\0';
  579 
  580     for (i = 0; i < login_count; i++) {
  581     char filename[FILENAME_MAX];
  582     struct stat st;
  583     char m[2] = { multi, '\0' };
  584 
  585     snprintf(filename, sizeof(filename), "%s%s", _PATH_DEV, u[i]->ut_line);
  586 
  587     if (stat(filename, &st) == -1)
  588         safe_strncat(line, "!", sizeof(line));
  589     else
  590         safe_strncat(line,
  591             (st.st_mode & S_IWGRP || st.st_mode & S_IWOTH) ? "1" : "0",
  592             sizeof(line));
  593 
  594     safe_strncat(line, m, sizeof(line));
  595     }
  596 
  597     if (line[0] == '\0')
  598     return "!";
  599 
  600     line[strlen(line) - 1] = '\0';
  601     return line;
  602 }
  603 
  604 /* Returns the users idle time in seconds. */
  605 static char *idle(UTMP **u, int multi)
  606 {
  607     static char line[LINE_MAX];
  608     time_t t;
  609     struct stat st;
  610     int i;
  611 
  612     line[0] = '\0';
  613 
  614     for (i = 0; i < login_count; i++) {
  615     char buf[FILENAME_MAX];
  616     char m[2] = { multi, '\0' };
  617 
  618     snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, u[i]->ut_line);
  619 
  620     if (stat(buf, &st) == -1) {
  621         safe_strncat(line, "!", sizeof(line));
  622         safe_strncat(line, m, sizeof(line));
  623         continue;
  624     }
  625 
  626 #ifdef HAVE_UTMPX_H
  627     if (u[i]->ut_tv.tv_sec > st.st_atime) {
  628 #else
  629     if (u[i]->ut_time > st.st_atime) {
  630 #endif
  631         safe_strncat(line, "-", sizeof(line));
  632         safe_strncat(line, m, sizeof(line));
  633         continue;
  634     }
  635 
  636     t = st.st_atime;
  637 
  638 #ifdef HAVE_UTMPX_H
  639     if (t < u[i]->ut_tv.tv_sec)
  640         t = u[i]->ut_tv.tv_sec;
  641 #else
  642     if (t < u[i]->ut_time)
  643         t = u[i]->ut_time;
  644 #endif
  645 
  646     snprintf(buf, sizeof(buf), "%lu", (now - t <= 0) ? 0 : now - t);
  647     safe_strncat(line, buf, sizeof(line));
  648     safe_strncat(line, m, sizeof(line));
  649     }
  650 
  651     if (line[0] == '\0')
  652     return "!";
  653 
  654     line[strlen(line) - 1] = '\0';
  655     return line;
  656 }
  657 
  658 /* This is output if the -h command line option is passed to the main program.
  659  */
  660 void ui_module_help()
  661 {
  662     printf("  Login information [-L (-%s)]:\n", LOGIN_OPTION_ORDER);
  663     printf("\t-y  tty\t\t\t\t");
  664     printf("-m  message status\n");
  665     printf("\t-t  login time stamp\t\t");
  666     printf("-d  duration in minutes\n");
  667     printf("\t-h  hostname\t\t\t");
  668     printf("-i  seconds idle\n");
  669     printf("\t-p  login process id\n");
  670     printf("\t-l  lastlog information"
  671        " (any of tt[y],[h]ostname,[t]ime, or [a]ll)\n\n");
  672     return;
  673 }
  674 
  675 /* This is the equivalent to main() only without argc and argv available. */
  676 int ui_module_exec(char ***s, const struct passwd *pw, const int multi,
  677            const int verbose, char *tf)
  678 {
  679     char *p = options;
  680     UTMP **u = NULL;
  681     char buf[255];
  682 
  683     login_count = 0;
  684     u = get_utmp(pw->pw_name);
  685     strings = *s;
  686 
  687     while (*p) {
  688     char line[LINE_MAX] = { '\0' };
  689     int i;
  690     char m[2] = { multi, '\0' };
  691 
  692     switch (*p) {
  693         case 'i':
  694         add_string(&strings, (u) ? idle(u, multi) : "!");
  695         break;
  696         case 'l':
  697         last_strings(lastlogin(pw, tf));
  698         break;
  699         case 'h':
  700         for (i = 0; i < login_count; i++) {
  701             if (u[i]->ut_host[0]
  702                 && isalnum((unsigned char) u[i]->ut_host[0]))
  703             safe_strncat(line, u[i]->ut_host, sizeof(line));
  704             else
  705             safe_strncat(line, "-", sizeof(line));
  706 
  707             safe_strncat(line, m, sizeof(line));
  708         }
  709 
  710         if (line[0] == '\0')
  711             strncpy(line, "!", sizeof(line));
  712         else
  713             line[strlen(line) - 1] = '\0';
  714 
  715         add_string(&strings, line);
  716         break;
  717         case 'y':
  718         for (i = 0; i < login_count; i++) {
  719             if (u[i]->ut_line[0])
  720             safe_strncat(line, u[i]->ut_line, sizeof(line));
  721             else
  722             safe_strncat(line, "!", sizeof(line));
  723 
  724             safe_strncat(line, m, sizeof(line));
  725         }
  726 
  727         if (line[0] == '\0')
  728             strncpy(line, "!", sizeof(line));
  729         else
  730             line[strlen(line) - 1] = '\0';
  731 
  732         add_string(&strings, line);
  733         break;
  734         case 'm':
  735         add_string(&strings, u ? msgstat(u, multi) : "!");
  736         break;
  737         case 't':
  738         for (i = 0; i < login_count; i++) {
  739 #ifdef HAVE_UTMPX_H
  740             safe_strncat(line, stamp(u[i]->ut_tv.tv_sec, tf), sizeof(line));
  741 #else
  742             safe_strncat(line, stamp(u[i]->ut_time, tf), sizeof(line));
  743 #endif
  744             safe_strncat(line, m, sizeof(line));
  745         }
  746 
  747         if (line[0] == '\0')
  748             strncpy(line, "!", sizeof(line));
  749         else
  750             line[strlen(line) - 1] = '\0';
  751 
  752         add_string(&strings, line);
  753         break;
  754         case 'd':
  755         for (i = 0; i < login_count; i++) {
  756 #ifdef HAVE_UTMPX_H
  757             if ((now - u[i]->ut_tv.tv_sec) > 60) {
  758             snprintf(buf, sizeof(buf), "%lu",
  759                  ((now - u[i]->ut_tv.tv_sec) / 60));
  760 #else
  761             if ((now - u[i]->ut_time) > 60) {
  762             snprintf(buf, sizeof(buf), "%lu",
  763                  ((now - u[i]->ut_time) / 60));
  764 #endif
  765             safe_strncat(line, buf, sizeof(line));
  766             }
  767             else
  768             safe_strncat(line, "-", sizeof(line));
  769 
  770             safe_strncat(line, m, sizeof(line));
  771         }
  772 
  773         if (line[0] == '\0')
  774             strncpy(line, "!", sizeof(line));
  775         else
  776             line[strlen(line) - 1] = '\0';
  777 
  778         add_string(&strings, line);
  779         break;
  780         case 'p':
  781 #ifdef HAVE_UTMPX_H
  782         for (i = 0; i < login_count; i++) {
  783             if (u[i]->ut_pid) {
  784             snprintf(buf, sizeof(buf), "%li", (long) u[i]->ut_pid);
  785             safe_strncat(line, buf, sizeof(line));
  786             }
  787             else
  788             safe_strncat(line, "!", sizeof(line));
  789 
  790             safe_strncat(line, m, sizeof(line));
  791         }
  792 
  793         if (line[0] == '\0')
  794             strncpy(line, "!", sizeof(line));
  795         else
  796             line[strlen(line) - 1] = '\0';
  797 
  798         add_string(&strings, line);
  799 #else
  800         add_string(&strings, (u) ? get_pid(pw->pw_uid, multi) : "!");
  801 #endif
  802         break;
  803         default:
  804         break;
  805     }
  806 
  807     p++;
  808     }
  809 
  810     free_logins(u);
  811     *s = strings;
  812     return EXIT_SUCCESS;
  813 }
  814 
  815 /* See if the last login options (-l) are valid. */
  816 static int parse_last_options(const char *args)
  817 {
  818     int i = 0;
  819 
  820     for (i = 0; i < strlen(args); i++) {
  821     switch (args[i]) {
  822         case 'y':
  823         case 'h':
  824         case 't':
  825         case 'a':
  826         break;
  827         default:
  828         return 1;
  829     }
  830     }
  831 
  832     return 0;
  833 }
  834 
  835 char *ui_module_options_init(char **defaults)
  836 {
  837     *defaults = "L";
  838     return LOGIN_OPTION_STRING;
  839 }
  840 
  841 /* Check module option validity. */
  842 int ui_module_options(int argc, char **argv)
  843 {
  844     int opt;
  845     char *p = options;
  846 
  847     while ((opt = getopt(argc, argv, LOGIN_OPTION_STRING)) != -1) {
  848     switch (opt) {
  849         case 'l':
  850         if (parse_last_options(optarg))
  851             return 1;
  852 
  853         last_options = optarg;
  854         break;
  855         case 'L':
  856         strncpy(options, LOGIN_OPTION_ORDER, sizeof(options));
  857         last_options = "a";
  858         return 0;
  859         case 'p':
  860         case 'd':
  861         case 'i':
  862         case 'm':
  863         case 'y':
  864         case 'h':
  865         case 't':
  866         break;
  867         case '?':
  868         warnx("login: invalid option -- %c", optopt);
  869         default:
  870         return 1;
  871     }
  872 
  873     *p++ = opt;
  874     *p = '\0';
  875     }
  876 
  877     return 0;
  878 }