Initial revision
[freeradius.git] / src / main / radwho.c
1 /*
2  * radwho       Show who is logged in on the terminal servers.
3  *              Can also be installed as fingerd on the UNIX
4  *              machine RADIUS runs on.
5  *
6  * Version:     @(#)radwho  1.25  27-Apr-1999  miquels@cistron.nl
7  *
8  * Patch:       22-Apr-1999     promera@cistron.nl
9  *              Added support for raw, comma seperated output
10  */
11
12 #include "autoconf.h"
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <string.h>
18 #include <pwd.h>
19 #include <errno.h>
20 #include <sys/stat.h>
21 #include <sys/utsname.h>
22 #include <time.h>
23 #include <ctype.h>
24 #include <netinet/in.h>
25
26 #if HAVE_MALLOC_H
27 #  include <malloc.h>
28 #endif
29
30 #include "sysutmp.h"
31 #include "radutmp.h"
32 #include "radiusd.h"
33
34 /*
35  *      FIXME: put in header file.
36  */
37 #define SYS_FINGER "/usr/bin/finger"
38 #define FINGER_DIR "/usr/local/lib/finger"
39
40 /*
41  *      Header above output and format.
42  */
43 char *hdr1 = 
44 "Login      Name              What  TTY  When      From      Location";
45 char *ufmt1 = "%-10.10s %-17.17s %-5.5s %-4.4s %-9.9s %-9.9s %-.16s%s";
46 char *ufmt1r = "%s,%s,%s,%s,%s,%s,%s%s";
47 char *rfmt1 = "%-10.10s %-17.17s %-5.5s S%-3d %-9.9s %-9.9s %-.19s%s";
48 char *rfmt1r = "%s,%s,%s,S%d,%s,%s,%s%s";
49
50 char *hdr2 = 
51 "Login      Port    What      When          From       Location";
52 char *ufmt2 = "%-10.10s %-7.7d %-9.9s %-13.13s %-10.10s %-.16s%s";
53 char *ufmt2r = "%s,%d,%s,%s,%s,%s%s";
54 char *rfmt2 = "%-10.10s S%-5d  %-9.9s %-13.13s %-10.10s %-.28s%s";
55 char *rfmt2r = "%s,S%d,%s,%s,%s,%s%s";
56
57 char *eol = "\n";
58 NAS *naslist;
59 int showname = -1;
60 int showptype = 0;
61 int showcid = 0;
62
63 /*
64  *      Safe popen. Ugh.
65  */
66 FILE *safe_popen(char *cmd, char *mode)
67 {
68         char            *p;
69         char            buf[1024];
70
71         /*
72          *      Change all suspect characters into a space.
73          */
74         strncpy(buf, cmd, sizeof(buf));
75         buf[sizeof(buf) - 1] = 0;
76         for (p = buf; *p; p++) {
77                 if (isalnum(*p))
78                         continue;
79                 if (strchr("@%-_ \t+:,./", *p) == NULL)
80                         *p = ' ';
81         }
82
83         return popen(buf, mode);
84 }
85
86 /*
87  *      Read the naslist file.
88  */
89 static NAS *my_read_naslist_file(char *file)
90 {
91         FILE    *fp;
92         char    buffer[256];
93         char    hostnm[128];
94         char    shortnm[32];
95         char    nastype[32];
96         int     lineno = 0;
97         NAS     *cl = NULL;
98         NAS     *c;
99
100         if ((fp = fopen(file, "r")) == NULL) {
101                 fprintf(stderr, "cannot open %s\n", file);
102                 return NULL;
103         }
104         while(fgets(buffer, 256, fp) != NULL) {
105                 lineno++;
106                 if (buffer[0] == '#' || buffer[0] == '\n')
107                         continue;
108                 shortnm[0] = 0;
109                 if (sscanf(buffer, "%s%s%s", hostnm, shortnm, nastype) < 2) {
110                         fprintf(stderr, "%s[%d]: syntax error\n", file, lineno);
111                         continue;
112                 }
113                 if ((c = malloc(sizeof(NAS))) == NULL) {
114                         fprintf(stderr, "%s[%d]: out of memory\n",
115                                 file, lineno);
116                         return NULL;
117                 }
118
119                 c->ipaddr = ip_getaddr(hostnm);
120                 strcpy(c->nastype, nastype);
121                 strcpy(c->shortname, shortnm);
122                 strcpy(c->longname, ip_hostname(c->ipaddr));
123
124                 c->next = cl;
125                 cl = c;
126         }
127         fclose(fp);
128
129         return cl;
130 }
131
132
133 /*
134  *      Print a file from FINGER_DIR. If the file is executable,
135  *      execute it instead. Return 0 if succesfull.
136  */
137 int ffile(char *arg)
138 {
139         FILE *fp;
140         char fn[1024];
141         int p = 0;
142         char *s;
143
144         sprintf(fn, "%s/%.32s", FINGER_DIR, arg);
145         if (access(fn, X_OK) == 0) {
146                 p = 1;
147                 sprintf(fn, "exec %s/%.32s 2>&1", FINGER_DIR, arg);
148                 fp = safe_popen(fn, "r");
149         } else fp = fopen(fn, "r");
150
151         if (fp == NULL) return -1;
152
153         while(fgets(fn, 1024, fp)) {
154                 if ((s = strchr(fn, '\n')) != NULL)
155                         *s = 0;
156                 fprintf(stdout, "%s\r\n", fn);
157         }
158         if (p)
159                 pclose(fp);
160         else
161                 fclose(fp);
162         fflush(stdout);
163         return 0;
164 }
165
166
167 /*
168  *      Execute the system finger and translate LF to CRLF.
169  */
170 void sys_finger(char *l)
171 {
172         FILE *fp;
173         char fn[1024];
174         char *p;
175
176         if (ffile(l) == 0)
177                 exit(0);
178
179         sprintf(fn, "exec %s %s", SYS_FINGER, l);
180         if ((fp = safe_popen(fn, "r")) == NULL) {
181                 printf("popen: %s\r\n", strerror(errno));
182                 exit(1);
183         }
184
185         while(fgets(fn, 1024, fp)) {
186                 if ((p = strchr(fn, '\n')) != NULL)
187                         *p = 0;
188                 fprintf(stdout, "%s\r\n", fn);
189         }
190         pclose(fp);
191         exit(0);
192 }
193
194
195 /*
196  *      Get fullname of a user.
197  */
198 char *fullname(char *login)
199 {
200         struct passwd *pwd;
201         char *s;
202
203         if ((pwd = getpwnam(login)) != NULL) {
204                 if ((s = strchr(pwd->pw_gecos, ',')) != NULL) *s = 0;
205                 return pwd->pw_gecos;
206         }
207         return login;
208 }
209
210 /*
211  *      Return protocol type.
212  */
213 char *proto(int id, int porttype)
214 {
215         static char buf[8];
216
217         if (showptype) {
218                 if (!strchr("ASITX", porttype))
219                         porttype = ' ';
220                 if (id == 'S')
221                         sprintf(buf, "SLP %c", porttype);
222                 else if (id == 'P')
223                         sprintf(buf, "PPP %c", porttype);
224                 else
225                         sprintf(buf, "shl %c", porttype);
226                 return buf;
227         }
228         if (id == 'S') return "SLIP";
229         if (id == 'P') return "PPP";
230         return "shell";
231 }
232
233 /*
234  *      Return a time in the form day hh:mm
235  */
236 char *dotime(time_t t)
237 {
238         char *s = ctime(&t);
239
240         if (showname) {
241                 strncpy(s + 4, s + 11, 5);
242                 s[9] = 0;
243         } else {
244                 strncpy(s + 4, s + 8, 8);
245                 s[12] = 0;
246         }
247
248         return s;
249 }
250
251 #if 0 /*UNUSED*/
252 /*
253  *      See how long a tty has been idle.
254  */
255 char *idletime(char *line)
256 {
257         char tty[16];
258         static char tmp[8];
259         time_t t;
260         struct stat st;
261         int hr, min, days;
262
263         if (line[0] == '/')
264                 strcpy(tty, "/dev/");
265         else
266                 tty[0] = 0;
267         strcat(tty, line);
268
269         tmp[0] = 0;
270         if (stat(tty, &st) == 0) {
271                 time(&t);
272                 t -= st.st_mtime;
273                 if (t >= 60) {
274                         min = (t / 60);
275                         hr = min / 24;
276                         days = hr / 24;
277                         min %= 60;
278                         hr %= 24;
279                         if (days > 0)
280                                 sprintf(tmp, "%dd", days);
281                         else
282                                 sprintf(tmp, "%2d:%02d", hr, min);
283                 }
284         }
285         return tmp;
286 }
287 #endif
288
289 /*
290  *      Shorten tty name.
291  */
292 char *ttyshort(char *tty)
293 {
294         static char tmp[16];
295
296         if (tty[0] == '/') tty += 5;
297
298         if (strncmp(tty, "tty", 3) == 0) {
299                 if (tty[3] >= '0' && tty[3] <= '9')
300                         sprintf(tmp, "v%s", tty + 3);
301                 else
302                         sprintf(tmp, "%s", tty + 3);
303                 return tmp;
304         }
305         if (strncmp(tty, "vc", 2) == 0) {
306                 sprintf(tmp, "v%s", tty + 2);
307                 return tmp;
308         }
309         if (strncmp(tty, "cu", 2) == 0) {
310                 return tmp + 2;
311         }
312         return "??";
313 }
314
315
316 /*
317  *      Find name of NAS
318  */
319 char *nasname(UINT4 ipaddr)
320 {
321         NAS *cl;
322
323         for(cl = naslist; cl; cl = cl->next)
324                 if (cl->ipaddr == ipaddr)
325                         break;
326         if (cl == NULL)
327                 return "";
328         if (cl->shortname[0])
329                 return cl->shortname;
330         return cl->longname;
331 }
332
333
334 /*
335  *      Print address of NAS.
336  */
337 char *hostname(UINT4 ipaddr)
338 {
339         if (ipaddr == 0 || ipaddr == (UINT4)-1 || ipaddr == (UINT4)-2)
340                 return "";
341         return ip_hostname(ipaddr);
342 }
343
344
345 /*
346  *      Print usage message and exit.
347  */
348 void usage(void)
349 {
350         fprintf(stderr, "Usage: radwho [-lhfnsipcr]\n");
351         fprintf(stderr, "       -l: show local (shell) users too\n");
352         fprintf(stderr, "       -h: hide shell users from radius\n");
353         fprintf(stderr, "       -f: give fingerd output\n");
354         fprintf(stderr, "       -n: no full name\n");
355         fprintf(stderr, "       -s: show full name\n");
356         fprintf(stderr, "       -i: show session ID\n");
357         fprintf(stderr, "       -p: show port type\n");
358         fprintf(stderr, "       -c: show caller ID, if available\n");
359         fprintf(stderr, "       -r: output as raw data\n");
360         exit(1);
361 }
362
363
364 /*
365  *      Main program, either pmwho or fingerd.
366  */
367 int main(int argc, char **argv)
368 {
369         FILE *fp;
370         struct radutmp rt;
371         struct utmp ut;
372         int hdrdone = 0;
373         char inbuf[128];
374         char myname[128];
375         char session_id[16];
376         int fingerd = 0;
377         int showlocal = 0;
378         int hideshell = 0;
379         int showsid = 0;
380         int rawoutput = 0;
381         char *p, *q;
382         int c;
383
384         while((c = getopt(argc, argv, "flhnsipcr")) != EOF) switch(c) {
385                 case 'f':
386                         fingerd++;
387                         showname = 0;
388                         break;
389                 case 'l':
390                         showlocal = 1;
391                         break;
392                 case 'h':
393                         hideshell = 1;
394                         break;
395                 case 'n':
396                         showname = 0;
397                         break;
398                 case 's':
399                         showname = 1;
400                         break;
401                 case 'i':
402                         showsid = 1;
403                         break;
404                 case 'p':
405                         showptype = 1;
406                         break;
407                 case 'c':
408                         showcid = 1;
409                         showname = 1;
410                         break;
411                 case 'r':
412                         rawoutput = 1;
413                         break;
414                 default:
415                         usage();
416                         break;
417         }
418
419         /*
420          *      Read the "naslist" file.
421          */
422         sprintf(inbuf, "%s/%s", RADIUS_DIR, RADIUS_NASLIST);
423         if ((naslist = my_read_naslist_file(inbuf)) == NULL)
424                 exit(1);
425
426         /*
427          *      See if we are "fingerd".
428          */
429         if (strstr(argv[0], "fingerd")) {
430                 fingerd++;
431                 eol = "\r\n";
432                 if (showname < 0) showname = 0;
433         }
434         if (showname < 0) showname = 1;
435
436         if (fingerd) {
437                 /*
438                  *      Read first line of the input.
439                  */
440                 fgets(inbuf, 128, stdin);
441                 p = inbuf;
442                 while(*p == ' ' || *p == '\t') p++;
443                 if (*p == '/' && *(p + 1)) p += 2;
444                 while(*p == ' ' || *p == '\t') p++;
445                 for(q = p; *q && *q != '\r' && *q != '\n'; q++)
446                         ;
447                 *q = 0;
448
449                 /*
450                  *      See if we fingered a specific user.
451                  */
452                 ffile("header");
453                 if (*p) sys_finger(p);
454         }
455
456         if (showlocal && (fp = fopen(UTMP_FILE, "r"))) {
457                 if (rawoutput == 0)
458                 {       
459                         fputs(showname ? hdr1 : hdr2, stdout);
460                         fputs(eol, stdout);
461                 }
462                 hdrdone = 1;
463
464                 /*
465                  *      Show the logged in UNIX users.
466                  */
467                 gethostname(myname, 128);
468                 while(fread(&ut, sizeof(ut), 1, fp) == 1) {
469 #ifdef USER_PROCESS
470                         if (ut.ut_user[0] && ut.ut_line[0] &&
471                                 ut.ut_type == USER_PROCESS) {
472 #else
473                         if (ut.ut_user[0] && ut.ut_line[0]) {
474 #endif
475                            if (showname)
476                                 printf((rawoutput == 0? ufmt1: ufmt1r),
477                                         ut.ut_name,
478                                         fullname(ut.ut_name),
479                                         "shell",
480                                         ttyshort(ut.ut_line),
481 #ifdef __svr4__
482                                         dotime(ut.ut_xtime),
483 #else
484                                         dotime(ut.ut_time),
485 #endif
486                                         ut.ut_host,
487                                         myname, eol);
488                             else
489                                 printf((rawoutput==0? ufmt2:ufmt2r),
490                                         ut.ut_name,
491                                         ttyshort(ut.ut_line),
492                                         "shell",
493 #ifdef __svr4__
494                                         dotime(ut.ut_xtime),
495 #else
496                                         dotime(ut.ut_time),
497 #endif
498                                         ut.ut_host,
499                                         myname, eol);
500                         }
501                 }
502                 fclose(fp);
503         }
504
505         /*
506          *      Show the users logged in on the terminal server(s).
507          */
508         if ((fp = fopen(RADUTMP, "r")) == NULL)
509                 return 0;
510
511         if (!hdrdone) {
512                 fputs(showname ? hdr1 : hdr2, stdout);
513                 fputs(eol, stdout);
514         }
515
516         while(fread(&rt, sizeof(rt), 1, fp) == 1) {
517                 if (rt.type == P_LOGIN) {
518                         /*
519                          *      We don't show shell users if we are
520                          *      fingerd, as we have done that above.
521                          */
522                         if (hideshell && !strchr("PCS", rt.proto))
523                                 continue;
524
525                         sprintf(session_id, "%.8s", rt.session_id);
526
527                         if (showname)
528                             printf((rawoutput == 0? rfmt1: rfmt1r),
529                                 rt.login,
530                                 showcid ? rt.caller_id :
531                                 (showsid? session_id : fullname(rt.login)),
532                                 proto(rt.proto, rt.porttype),
533                                 rt.nas_port,
534                                 dotime(rt.time),
535                                 nasname(rt.nas_address),
536                                 hostname(rt.framed_address), eol);
537                         else
538                             printf((rawoutput == 0? rfmt2: rfmt2r),
539                                 rt.login,
540                                 rt.nas_port,
541                                 proto(rt.proto, rt.porttype),
542                                 dotime(rt.time),
543                                 nasname(rt.nas_address),
544                                 hostname(rt.framed_address), eol);
545                 }
546         }
547         fflush(stdout);
548         fflush(stderr);
549         fclose(fp);
550
551         return 0;
552 }
553