Commit | Line | Data |
---|---|---|
22e155fc DF |
1 | /* |
2 | * Copyright (c) 1980 Regents of the University of California. | |
3 | * All rights reserved. The Berkeley software License Agreement | |
4 | * specifies the terms and conditions for redistribution. | |
5 | */ | |
6 | ||
7 | #ifndef lint | |
8 | char copyright[] = | |
9 | "@(#) Copyright (c) 1980 Regents of the University of California.\n\ | |
10 | All rights reserved.\n"; | |
11 | #endif not lint | |
12 | ||
840fc587 | 13 | #ifndef lint |
a243bfdf | 14 | static char sccsid[] = "@(#)finger.c 5.4 (Berkeley) %G%"; |
22e155fc | 15 | #endif not lint |
2e61617d | 16 | |
f5ebcf9e EW |
17 | /* |
18 | * This is a finger program. It prints out useful information about users | |
19 | * by digging it up from various system files. It is not very portable | |
20 | * because the most useful parts of the information (the full user name, | |
21 | * office, and phone numbers) are all stored in the VAX-unused gecos field | |
22 | * of /etc/passwd, which, unfortunately, other UNIXes use for other things. | |
2e61617d | 23 | * |
f5ebcf9e EW |
24 | * There are three output formats, all of which give login name, teletype |
25 | * line number, and login time. The short output format is reminiscent | |
26 | * of finger on ITS, and gives one line of information per user containing | |
27 | * in addition to the minimum basic requirements (MBR), the full name of | |
28 | * the user, his idle time and office location and phone number. The | |
29 | * quick style output is UNIX who-like, giving only name, teletype and | |
30 | * login time. Finally, the long style output give the same information | |
31 | * as the short (in more legible format), the home directory and shell | |
32 | * of the user, and, if it exits, a copy of the file .plan in the users | |
33 | * home directory. Finger may be called with or without a list of people | |
34 | * to finger -- if no list is given, all the people currently logged in | |
35 | * are fingered. | |
2e61617d | 36 | * |
f5ebcf9e | 37 | * The program is validly called by one of the following: |
2e61617d BJ |
38 | * |
39 | * finger {short form list of users} | |
40 | * finger -l {long form list of users} | |
41 | * finger -b {briefer long form list of users} | |
42 | * finger -q {quick list of users} | |
43 | * finger -i {quick list of users with idle times} | |
44 | * finger namelist {long format list of specified users} | |
45 | * finger -s namelist {short format list of specified users} | |
46 | * finger -w namelist {narrow short format list of specified users} | |
47 | * | |
f5ebcf9e EW |
48 | * where 'namelist' is a list of users login names. |
49 | * The other options can all be given after one '-', or each can have its | |
50 | * own '-'. The -f option disables the printing of headers for short and | |
51 | * quick outputs. The -b option briefens long format outputs. The -p | |
52 | * option turns off plans for long format outputs. | |
2e61617d BJ |
53 | */ |
54 | ||
f5ebcf9e EW |
55 | #include <sys/types.h> |
56 | #include <sys/stat.h> | |
57 | #include <utmp.h> | |
58 | #include <sys/signal.h> | |
59 | #include <pwd.h> | |
60 | #include <stdio.h> | |
61 | #include <lastlog.h> | |
62 | #include <ctype.h> | |
63 | #include <sys/time.h> | |
64 | #include <sys/socket.h> | |
65 | #include <netinet/in.h> | |
66 | #include <netdb.h> | |
67 | ||
68 | #define ASTERISK '*' /* ignore this in real name */ | |
69 | #define COMMA ',' /* separator in pw_gecos field */ | |
70 | #define COMMAND '-' /* command line flag char */ | |
71 | #define CORY 'C' /* cory hall office */ | |
72 | #define EVANS 'E' /* evans hall office */ | |
73 | #define SAMENAME '&' /* repeat login name in real name */ | |
74 | #define TALKABLE 0222 /* tty is writable if 222 mode */ | |
75 | ||
76 | struct utmp user; | |
77 | #define NMAX sizeof(user.ut_name) | |
78 | #define LMAX sizeof(user.ut_line) | |
79 | #define HMAX sizeof(user.ut_host) | |
80 | ||
81 | struct person { /* one for each person fingered */ | |
82 | char *name; /* name */ | |
83 | char tty[LMAX+1]; /* null terminated tty line */ | |
84 | char host[HMAX+1]; /* null terminated remote host name */ | |
85 | long loginat; /* time of (last) login */ | |
86 | long idletime; /* how long idle (if logged in) */ | |
87 | char *realname; /* pointer to full name */ | |
88 | char *office; /* pointer to office name */ | |
89 | char *officephone; /* pointer to office phone no. */ | |
90 | char *homephone; /* pointer to home phone no. */ | |
91 | char *random; /* for any random stuff in pw_gecos */ | |
92 | struct passwd *pwd; /* structure of /etc/passwd stuff */ | |
93 | char loggedin; /* person is logged in */ | |
94 | char writable; /* tty is writable */ | |
95 | char original; /* this is not a duplicate entry */ | |
96 | struct person *link; /* link to next person */ | |
2e61617d BJ |
97 | }; |
98 | ||
f5ebcf9e EW |
99 | char LASTLOG[] = "/usr/adm/lastlog"; /* last login info */ |
100 | char USERLOG[] = "/etc/utmp"; /* who is logged in */ | |
101 | char PLAN[] = "/.plan"; /* what plan file is */ | |
102 | char PROJ[] = "/.project"; /* what project file */ | |
103 | ||
104 | int unbrief = 1; /* -b option default */ | |
105 | int header = 1; /* -f option default */ | |
106 | int hack = 1; /* -h option default */ | |
107 | int idle = 0; /* -i option default */ | |
108 | int large = 0; /* -l option default */ | |
109 | int match = 1; /* -m option default */ | |
110 | int plan = 1; /* -p option default */ | |
111 | int unquick = 1; /* -q option default */ | |
112 | int small = 0; /* -s option default */ | |
113 | int wide = 1; /* -w option default */ | |
114 | ||
115 | int unshort; | |
116 | int lf; /* LASTLOG file descriptor */ | |
117 | struct person *person1; /* list of people */ | |
118 | long tloc; /* current time */ | |
119 | ||
120 | struct passwd *pwdcopy(); | |
121 | char *strcpy(); | |
122 | char *malloc(); | |
123 | char *ctime(); | |
124 | ||
125 | main(argc, argv) | |
126 | int argc; | |
127 | register char **argv; | |
2e61617d | 128 | { |
f5ebcf9e EW |
129 | FILE *fp; |
130 | register char *s; | |
131 | ||
132 | /* parse command line for (optional) arguments */ | |
133 | while (*++argv && **argv == COMMAND) | |
134 | for (s = *argv + 1; *s; s++) | |
135 | switch (*s) { | |
136 | case 'b': | |
137 | unbrief = 0; | |
138 | break; | |
139 | case 'f': | |
140 | header = 0; | |
141 | break; | |
142 | case 'h': | |
143 | hack = 0; | |
144 | break; | |
145 | case 'i': | |
146 | idle = 1; | |
147 | unquick = 0; | |
148 | break; | |
149 | case 'l': | |
150 | large = 1; | |
151 | break; | |
152 | case 'm': | |
153 | match = 0; | |
154 | break; | |
155 | case 'p': | |
156 | plan = 0; | |
157 | break; | |
158 | case 'q': | |
159 | unquick = 0; | |
160 | break; | |
161 | case 's': | |
162 | small = 1; | |
163 | break; | |
164 | case 'w': | |
165 | wide = 0; | |
166 | break; | |
167 | default: | |
168 | fprintf(stderr, "Usage: finger [-bfhilmpqsw] [login1 [login2 ...] ]\n"); | |
169 | exit(1); | |
2e61617d | 170 | } |
f5ebcf9e EW |
171 | if (unquick || idle) |
172 | time(&tloc); | |
173 | /* | |
174 | * *argv == 0 means no names given | |
175 | */ | |
176 | if (*argv == 0) | |
177 | doall(); | |
178 | else | |
179 | donames(argv); | |
711974ba EW |
180 | if (person1) |
181 | print(); | |
f5ebcf9e EW |
182 | exit(0); |
183 | } | |
2e61617d | 184 | |
f5ebcf9e EW |
185 | doall() |
186 | { | |
187 | register struct person *p; | |
188 | register struct passwd *pw; | |
189 | int uf; | |
190 | char name[NMAX + 1]; | |
191 | ||
192 | unshort = large; | |
193 | if ((uf = open(USERLOG, 0)) < 0) { | |
194 | fprintf(stderr, "finger: error opening %s\n", USERLOG); | |
195 | exit(2); | |
196 | } | |
197 | if (unquick) { | |
198 | extern _pw_stayopen; | |
2e61617d | 199 | |
2e61617d | 200 | setpwent(); |
f5ebcf9e | 201 | _pw_stayopen = 1; |
2e61617d | 202 | fwopen(); |
f5ebcf9e EW |
203 | } |
204 | while (read(uf, (char *)&user, sizeof user) == sizeof user) { | |
205 | if (user.ut_name[0] == 0) | |
206 | continue; | |
207 | if (person1 == 0) | |
208 | p = person1 = (struct person *) malloc(sizeof *p); | |
209 | else { | |
210 | p->link = (struct person *) malloc(sizeof *p); | |
2e61617d | 211 | p = p->link; |
2e61617d | 212 | } |
f5ebcf9e EW |
213 | bcopy(user.ut_name, name, NMAX); |
214 | name[NMAX] = 0; | |
215 | bcopy(user.ut_line, p->tty, LMAX); | |
216 | p->tty[LMAX] = 0; | |
217 | bcopy(user.ut_host, p->host, HMAX); | |
218 | p->host[HMAX] = 0; | |
219 | p->loginat = user.ut_time; | |
220 | p->pwd = 0; | |
221 | p->loggedin = 1; | |
222 | if (unquick && (pw = getpwnam(name))) { | |
223 | p->pwd = pwdcopy(pw); | |
224 | decode(p); | |
225 | p->name = p->pwd->pw_name; | |
226 | } else | |
227 | p->name = strcpy(malloc(strlen(name) + 1), name); | |
228 | } | |
229 | if (unquick) { | |
2e61617d BJ |
230 | fwclose(); |
231 | endpwent(); | |
2e61617d | 232 | } |
f5ebcf9e EW |
233 | close(uf); |
234 | if (person1 == 0) { | |
235 | printf("No one logged on\n"); | |
711974ba | 236 | return; |
f5ebcf9e EW |
237 | } |
238 | p->link = 0; | |
239 | } | |
240 | ||
241 | donames(argv) | |
242 | char **argv; | |
243 | { | |
244 | register struct person *p; | |
245 | register struct passwd *pw; | |
246 | int uf; | |
247 | ||
248 | /* | |
249 | * get names from command line and check to see if they're | |
250 | * logged in | |
251 | */ | |
252 | unshort = !small; | |
253 | for (; *argv != 0; argv++) { | |
254 | if (netfinger(*argv)) | |
255 | continue; | |
256 | if (person1 == 0) | |
257 | p = person1 = (struct person *) malloc(sizeof *p); | |
258 | else { | |
259 | p->link = (struct person *) malloc(sizeof *p); | |
260 | p = p->link; | |
b474790b | 261 | } |
f5ebcf9e | 262 | p->name = *argv; |
2e61617d | 263 | p->loggedin = 0; |
f5ebcf9e EW |
264 | p->original = 1; |
265 | p->pwd = 0; | |
266 | } | |
711974ba EW |
267 | if (person1 == 0) |
268 | return; | |
f5ebcf9e EW |
269 | p->link = 0; |
270 | /* | |
271 | * if we are doing it, read /etc/passwd for the useful info | |
272 | */ | |
273 | if (unquick) { | |
2e61617d | 274 | setpwent(); |
f5ebcf9e EW |
275 | if (!match) { |
276 | extern _pw_stayopen; | |
277 | ||
278 | _pw_stayopen = 1; | |
279 | for (p = person1; p != 0; p = p->link) | |
280 | if (pw = getpwnam(p->name)) | |
281 | p->pwd = pwdcopy(pw); | |
282 | } else while ((pw = getpwent()) != 0) { | |
283 | for (p = person1; p != 0; p = p->link) { | |
284 | if (!p->original) | |
285 | continue; | |
286 | if (strcmp(p->name, pw->pw_name) != 0 && | |
287 | !matchcmp(pw->pw_gecos, pw->pw_name, p->name)) | |
288 | continue; | |
289 | if (p->pwd == 0) | |
290 | p->pwd = pwdcopy(pw); | |
291 | else { | |
292 | struct person *new; | |
293 | /* | |
294 | * handle multiple login names, insert | |
295 | * new "duplicate" entry behind | |
296 | */ | |
297 | new = (struct person *) | |
298 | malloc(sizeof *new); | |
299 | new->pwd = pwdcopy(pw); | |
300 | new->name = p->name; | |
301 | new->original = 1; | |
302 | new->loggedin = 0; | |
303 | new->link = p->link; | |
304 | p->original = 0; | |
305 | p->link = new; | |
306 | p = new; | |
307 | } | |
2e61617d | 308 | } |
2e61617d BJ |
309 | } |
310 | endpwent(); | |
f5ebcf9e EW |
311 | } |
312 | /* Now get login information */ | |
313 | if ((uf = open(USERLOG, 0)) < 0) { | |
314 | fprintf(stderr, "finger: error opening %s\n", USERLOG); | |
315 | exit(2); | |
316 | } | |
317 | while (read(uf, (char *)&user, sizeof user) == sizeof user) { | |
318 | if (*user.ut_name == 0) | |
319 | continue; | |
320 | for (p = person1; p != 0; p = p->link) { | |
321 | if (p->loggedin == 2) | |
322 | continue; | |
323 | if (strncmp(p->pwd ? p->pwd->pw_name : p->name, | |
324 | user.ut_name, NMAX) != 0) | |
325 | continue; | |
326 | if (p->loggedin == 0) { | |
327 | bcopy(user.ut_line, p->tty, LMAX); | |
328 | p->tty[LMAX] = 0; | |
329 | bcopy(user.ut_host, p->host, HMAX); | |
330 | p->host[HMAX] = 0; | |
331 | p->loginat = user.ut_time; | |
332 | p->loggedin = 1; | |
333 | } else { /* p->loggedin == 1 */ | |
334 | struct person *new; | |
335 | new = (struct person *) malloc(sizeof *new); | |
336 | new->name = p->name; | |
337 | bcopy(user.ut_line, new->tty, LMAX); | |
338 | new->tty[LMAX] = 0; | |
339 | bcopy(user.ut_host, new->host, HMAX); | |
340 | new->host[HMAX] = 0; | |
341 | new->loginat = user.ut_time; | |
342 | new->pwd = p->pwd; | |
343 | new->loggedin = 1; | |
344 | new->original = 0; | |
345 | new->link = p->link; | |
346 | p->loggedin = 2; | |
347 | p->link = new; | |
348 | p = new; | |
2e61617d | 349 | } |
2e61617d | 350 | } |
f5ebcf9e EW |
351 | } |
352 | close(uf); | |
353 | if (unquick) { | |
2e61617d | 354 | fwopen(); |
f5ebcf9e EW |
355 | for (p = person1; p != 0; p = p->link) |
356 | decode(p); | |
2e61617d | 357 | fwclose(); |
2e61617d | 358 | } |
f5ebcf9e | 359 | } |
2e61617d | 360 | |
f5ebcf9e EW |
361 | print() |
362 | { | |
363 | register FILE *fp; | |
364 | register struct person *p; | |
365 | register char *s; | |
366 | register c; | |
367 | ||
368 | /* | |
369 | * print out what we got | |
370 | */ | |
371 | if (header) { | |
372 | if (unquick) { | |
373 | if (!unshort) | |
374 | if (wide) | |
375 | printf("Login Name TTY Idle When Office\n"); | |
376 | else | |
377 | printf("Login TTY Idle When Office\n"); | |
378 | } else { | |
379 | printf("Login TTY When"); | |
380 | if (idle) | |
381 | printf(" Idle"); | |
382 | putchar('\n'); | |
2e61617d | 383 | } |
2e61617d | 384 | } |
f5ebcf9e EW |
385 | for (p = person1; p != 0; p = p->link) { |
386 | if (!unquick) { | |
387 | quickprint(p); | |
388 | continue; | |
389 | } | |
390 | if (!unshort) { | |
391 | shortprint(p); | |
392 | continue; | |
393 | } | |
394 | personprint(p); | |
395 | if (p->pwd != 0) { | |
396 | if (hack) { | |
397 | s = malloc(strlen(p->pwd->pw_dir) + | |
398 | sizeof PROJ); | |
399 | strcpy(s, p->pwd->pw_dir); | |
400 | strcat(s, PROJ); | |
401 | if ((fp = fopen(s, "r")) != 0) { | |
402 | printf("Project: "); | |
403 | while ((c = getc(fp)) != EOF) { | |
404 | if (c == '\n') | |
405 | break; | |
406 | putchar(c); | |
407 | } | |
408 | fclose(fp); | |
409 | putchar('\n'); | |
2e61617d | 410 | } |
f5ebcf9e | 411 | free(s); |
2e61617d | 412 | } |
f5ebcf9e EW |
413 | if (plan) { |
414 | s = malloc(strlen(p->pwd->pw_dir) + | |
415 | sizeof PLAN); | |
416 | strcpy(s, p->pwd->pw_dir); | |
417 | strcat(s, PLAN); | |
418 | if ((fp = fopen(s, "r")) == 0) | |
419 | printf("No Plan.\n"); | |
420 | else { | |
421 | printf("Plan:\n"); | |
422 | while ((c = getc(fp)) != EOF) | |
423 | putchar(c); | |
424 | fclose(fp); | |
2e61617d | 425 | } |
f5ebcf9e | 426 | free(s); |
2e61617d | 427 | } |
2e61617d | 428 | } |
f5ebcf9e EW |
429 | if (p->link != 0) |
430 | putchar('\n'); | |
431 | } | |
2e61617d BJ |
432 | } |
433 | ||
f5ebcf9e EW |
434 | /* |
435 | * Duplicate a pwd entry. | |
436 | * Note: Only the useful things (what the program currently uses) are copied. | |
2e61617d | 437 | */ |
f5ebcf9e EW |
438 | struct passwd * |
439 | pwdcopy(pfrom) | |
440 | register struct passwd *pfrom; | |
2e61617d | 441 | { |
f5ebcf9e EW |
442 | register struct passwd *pto; |
443 | ||
444 | pto = (struct passwd *) malloc(sizeof *pto); | |
445 | #define savestr(s) strcpy(malloc(strlen(s) + 1), s) | |
446 | pto->pw_name = savestr(pfrom->pw_name); | |
2e61617d | 447 | pto->pw_uid = pfrom->pw_uid; |
f5ebcf9e EW |
448 | pto->pw_gecos = savestr(pfrom->pw_gecos); |
449 | pto->pw_dir = savestr(pfrom->pw_dir); | |
450 | pto->pw_shell = savestr(pfrom->pw_shell); | |
451 | #undef savestr | |
452 | return pto; | |
2e61617d BJ |
453 | } |
454 | ||
f5ebcf9e EW |
455 | /* |
456 | * print out information on quick format giving just name, tty, login time | |
457 | * and idle time if idle is set. | |
2e61617d | 458 | */ |
f5ebcf9e EW |
459 | quickprint(pers) |
460 | register struct person *pers; | |
2e61617d | 461 | { |
f5ebcf9e EW |
462 | printf("%-*.*s ", NMAX, NMAX, pers->name); |
463 | if (pers->loggedin) { | |
464 | if (idle) { | |
465 | findidle(pers); | |
466 | printf("%c%-*s %-16.16s", pers->writable ? ' ' : '*', | |
467 | LMAX, pers->tty, ctime(&pers->loginat)); | |
468 | ltimeprint(" ", &pers->idletime, ""); | |
469 | } else | |
470 | printf(" %-*s %-16.16s", LMAX, | |
471 | pers->tty, ctime(&pers->loginat)); | |
472 | putchar('\n'); | |
473 | } else | |
474 | printf(" Not Logged In\n"); | |
2e61617d BJ |
475 | } |
476 | ||
f5ebcf9e EW |
477 | /* |
478 | * print out information in short format, giving login name, full name, | |
479 | * tty, idle time, login time, office location and phone. | |
2e61617d | 480 | */ |
f5ebcf9e EW |
481 | shortprint(pers) |
482 | register struct person *pers; | |
2e61617d | 483 | { |
f5ebcf9e EW |
484 | char *p; |
485 | char dialup; | |
2e61617d | 486 | |
f5ebcf9e EW |
487 | if (pers->pwd == 0) { |
488 | printf("%-15s ???\n", pers->name); | |
489 | return; | |
2e61617d | 490 | } |
f5ebcf9e | 491 | printf("%-*s", NMAX, pers->pwd->pw_name); |
2e61617d | 492 | dialup = 0; |
f5ebcf9e EW |
493 | if (wide) { |
494 | if (pers->realname) | |
495 | printf(" %-20.20s", pers->realname); | |
496 | else | |
497 | printf(" ??? "); | |
2e61617d | 498 | } |
f5ebcf9e EW |
499 | putchar(' '); |
500 | if (pers->loggedin && !pers->writable) | |
501 | putchar('*'); | |
502 | else | |
503 | putchar(' '); | |
504 | if (*pers->tty) { | |
505 | if (pers->tty[0] == 't' && pers->tty[1] == 't' && | |
506 | pers->tty[2] == 'y') { | |
507 | if (pers->tty[3] == 'd' && pers->loggedin) | |
508 | dialup = 1; | |
509 | printf("%-2.2s ", pers->tty + 3); | |
510 | } else | |
511 | printf("%-2.2s ", pers->tty); | |
512 | } else | |
513 | printf(" "); | |
514 | p = ctime(&pers->loginat); | |
515 | if (pers->loggedin) { | |
516 | stimeprint(&pers->idletime); | |
517 | printf(" %3.3s %-5.5s ", p, p + 11); | |
518 | } else if (pers->loginat == 0) | |
519 | printf(" < . . . . >"); | |
27cee9bc | 520 | else if (tloc - pers->loginat >= 180 * 24 * 60 * 60) |
f5ebcf9e | 521 | printf(" <%-6.6s, %-4.4s>", p + 4, p + 20); |
27cee9bc | 522 | else |
f5ebcf9e EW |
523 | printf(" <%-12.12s>", p + 4); |
524 | if (dialup && pers->homephone) | |
525 | printf(" %20s", pers->homephone); | |
526 | else { | |
527 | if (pers->office) | |
528 | printf(" %-11.11s", pers->office); | |
529 | else if (pers->officephone || pers->homephone) | |
530 | printf(" "); | |
531 | if (pers->officephone) | |
532 | printf(" %s", pers->officephone); | |
533 | else if (pers->homephone) | |
534 | printf(" %s", pers->homephone); | |
2e61617d | 535 | } |
f5ebcf9e | 536 | putchar('\n'); |
2e61617d BJ |
537 | } |
538 | ||
f5ebcf9e EW |
539 | /* |
540 | * print out a person in long format giving all possible information. | |
541 | * directory and shell are inhibited if unbrief is clear. | |
2e61617d | 542 | */ |
f5ebcf9e EW |
543 | personprint(pers) |
544 | register struct person *pers; | |
2e61617d | 545 | { |
f5ebcf9e EW |
546 | if (pers->pwd == 0) { |
547 | printf("Login name: %-10s\t\t\tIn real life: ???\n", | |
548 | pers->name); | |
549 | return; | |
2e61617d | 550 | } |
f5ebcf9e EW |
551 | printf("Login name: %-10s", pers->pwd->pw_name); |
552 | if (pers->loggedin && !pers->writable) | |
553 | printf(" (messages off) "); | |
554 | else | |
555 | printf(" "); | |
556 | if (pers->realname) | |
557 | printf("In real life: %s", pers->realname); | |
558 | if (pers->office) { | |
559 | printf("\nOffice: %-.11s", pers->office); | |
560 | if (pers->officephone) { | |
561 | printf(", %s", pers->officephone); | |
562 | if (pers->homephone) | |
563 | printf("\t\tHome phone: %s", pers->homephone); | |
564 | else if (pers->random) | |
565 | printf("\t\t%s", pers->random); | |
566 | } else | |
567 | if (pers->homephone) | |
568 | printf("\t\t\tHome phone: %s", pers->homephone); | |
569 | else if (pers->random) | |
570 | printf("\t\t\t%s", pers->random); | |
571 | } else if (pers->officephone) { | |
572 | printf("\nPhone: %s", pers->officephone); | |
573 | if (pers->homephone) | |
574 | printf(", %s", pers->homephone); | |
575 | if (pers->random) | |
576 | printf(", %s", pers->random); | |
577 | } else if (pers->homephone) { | |
578 | printf("\nPhone: %s", pers->homephone); | |
579 | if (pers->random) | |
580 | printf(", %s", pers->random); | |
581 | } else if (pers->random) | |
582 | printf("\n%s", pers->random); | |
583 | if (unbrief) { | |
584 | printf("\nDirectory: %-25s", pers->pwd->pw_dir); | |
585 | if (*pers->pwd->pw_shell) | |
586 | printf("\tShell: %-s", pers->pwd->pw_shell); | |
2e61617d | 587 | } |
f5ebcf9e EW |
588 | if (pers->loggedin) { |
589 | register char *ep = ctime(&pers->loginat); | |
590 | if (*pers->host) { | |
591 | printf("\nOn since %15.15s on %s from %s", | |
592 | &ep[4], pers->tty, pers->host); | |
593 | ltimeprint("\n", &pers->idletime, " Idle Time"); | |
594 | } else { | |
595 | printf("\nOn since %15.15s on %-*s", | |
596 | &ep[4], LMAX, pers->tty); | |
597 | ltimeprint("\t", &pers->idletime, " Idle Time"); | |
2e61617d | 598 | } |
f5ebcf9e EW |
599 | } else if (pers->loginat == 0) |
600 | printf("\nNever logged in."); | |
27cee9bc | 601 | else if (tloc - pers->loginat > 180 * 24 * 60 * 60) { |
f5ebcf9e EW |
602 | register char *ep = ctime(&pers->loginat); |
603 | printf("\nLast login %10.10s, %4.4s on %s", | |
604 | ep, ep+20, pers->tty); | |
605 | if (*pers->host) | |
606 | printf(" from %s", pers->host); | |
607 | } else { | |
608 | register char *ep = ctime(&pers->loginat); | |
609 | printf("\nLast login %16.16s on %s", ep, pers->tty); | |
610 | if (*pers->host) | |
611 | printf(" from %s", pers->host); | |
27cee9bc | 612 | } |
f5ebcf9e | 613 | putchar('\n'); |
2e61617d BJ |
614 | } |
615 | ||
2e61617d BJ |
616 | /* |
617 | * very hacky section of code to format phone numbers. filled with | |
618 | * magic constants like 4, 7 and 10. | |
619 | */ | |
f5ebcf9e EW |
620 | char * |
621 | phone(s, len, alldigits) | |
622 | register char *s; | |
623 | int len; | |
624 | char alldigits; | |
2e61617d | 625 | { |
f5ebcf9e EW |
626 | char fonebuf[15]; |
627 | register char *p = fonebuf; | |
628 | register i; | |
629 | ||
630 | if (!alldigits) | |
631 | return (strcpy(malloc(len + 1), s)); | |
632 | switch (len) { | |
633 | case 4: | |
634 | *p++ = ' '; | |
635 | *p++ = 'x'; | |
636 | *p++ = '2'; | |
637 | *p++ = '-'; | |
638 | for (i = 0; i < 4; i++) | |
639 | *p++ = *s++; | |
2e61617d | 640 | break; |
172389f0 JB |
641 | case 5: |
642 | *p++ = ' '; | |
643 | *p++ = 'x'; | |
644 | *p++ = *s++; | |
645 | *p++ = '-'; | |
646 | for (i = 0; i < 4; i++) | |
647 | *p++ = *s++; | |
648 | break; | |
f5ebcf9e EW |
649 | case 7: |
650 | for (i = 0; i < 3; i++) | |
651 | *p++ = *s++; | |
652 | *p++ = '-'; | |
653 | for (i = 0; i < 4; i++) | |
654 | *p++ = *s++; | |
2e61617d | 655 | break; |
f5ebcf9e EW |
656 | case 10: |
657 | for (i = 0; i < 3; i++) | |
658 | *p++ = *s++; | |
659 | *p++ = '-'; | |
660 | for (i = 0; i < 3; i++) | |
661 | *p++ = *s++; | |
662 | *p++ = '-'; | |
663 | for (i = 0; i < 4; i++) | |
664 | *p++ = *s++; | |
2e61617d | 665 | break; |
f5ebcf9e EW |
666 | case 0: |
667 | return 0; | |
668 | default: | |
669 | return (strcpy(malloc(len + 1), s)); | |
2e61617d | 670 | } |
f5ebcf9e EW |
671 | *p++ = 0; |
672 | return (strcpy(malloc(p - fonebuf), fonebuf)); | |
2e61617d BJ |
673 | } |
674 | ||
f5ebcf9e EW |
675 | /* |
676 | * decode the information in the gecos field of /etc/passwd | |
2e61617d | 677 | */ |
f5ebcf9e EW |
678 | decode(pers) |
679 | register struct person *pers; | |
2e61617d | 680 | { |
f5ebcf9e EW |
681 | char buffer[256]; |
682 | register char *bp, *gp, *lp; | |
683 | int alldigits; | |
684 | int hasspace; | |
685 | int len; | |
686 | ||
687 | pers->realname = 0; | |
688 | pers->office = 0; | |
689 | pers->officephone = 0; | |
690 | pers->homephone = 0; | |
691 | pers->random = 0; | |
692 | if (pers->pwd == 0) | |
693 | return; | |
694 | gp = pers->pwd->pw_gecos; | |
695 | bp = buffer; | |
696 | if (*gp == ASTERISK) | |
2e61617d | 697 | gp++; |
f5ebcf9e EW |
698 | while (*gp && *gp != COMMA) /* name */ |
699 | if (*gp == SAMENAME) { | |
700 | lp = pers->pwd->pw_name; | |
701 | if (islower(*lp)) | |
702 | *bp++ = toupper(*lp++); | |
703 | while (*bp++ = *lp++) | |
704 | ; | |
705 | bp--; | |
706 | gp++; | |
707 | } else | |
708 | *bp++ = *gp++; | |
709 | *bp++ = 0; | |
710 | if ((len = bp - buffer) > 1) | |
711 | pers->realname = strcpy(malloc(len), buffer); | |
712 | if (*gp == COMMA) { /* office */ | |
713 | gp++; | |
714 | hasspace = 0; | |
715 | bp = buffer; | |
716 | while (*gp && *gp != COMMA) { | |
717 | *bp = *gp++; | |
718 | if (*bp == ' ') | |
719 | hasspace = 1; | |
720 | /* leave 5 for Cory and Evans expansion */ | |
721 | if (bp < buffer + sizeof buffer - 6) | |
722 | bp++; | |
2e61617d | 723 | } |
f5ebcf9e EW |
724 | *bp = 0; |
725 | len = bp - buffer; | |
726 | bp--; /* point to last character */ | |
727 | if (hasspace || len == 0) | |
728 | len++; | |
729 | else if (*bp == CORY) { | |
730 | strcpy(bp, " Cory"); | |
731 | len += 5; | |
732 | } else if (*bp == EVANS) { | |
733 | strcpy(bp, " Evans"); | |
734 | len += 6; | |
735 | } else | |
736 | len++; | |
737 | if (len > 1) | |
738 | pers->office = strcpy(malloc(len), buffer); | |
739 | } | |
740 | if (*gp == COMMA) { /* office phone */ | |
2e61617d | 741 | gp++; |
f5ebcf9e | 742 | bp = buffer; |
2e61617d | 743 | alldigits = 1; |
f5ebcf9e EW |
744 | while (*gp && *gp != COMMA) { |
745 | *bp = *gp++; | |
746 | if (!isdigit(*bp)) | |
747 | alldigits = 0; | |
748 | if (bp < buffer + sizeof buffer - 1) | |
749 | bp++; | |
2e61617d | 750 | } |
f5ebcf9e EW |
751 | *bp = 0; |
752 | pers->officephone = phone(buffer, bp - buffer, alldigits); | |
753 | } | |
754 | if (*gp == COMMA) { /* home phone */ | |
755 | gp++; | |
756 | bp = buffer; | |
757 | alldigits = 1; | |
758 | while (*gp && *gp != COMMA) { | |
2e61617d | 759 | *bp = *gp++; |
f5ebcf9e EW |
760 | if (!isdigit(*bp)) |
761 | alldigits = 0; | |
762 | if (bp < buffer + sizeof buffer - 1) | |
2e61617d | 763 | bp++; |
2e61617d | 764 | } |
f5ebcf9e EW |
765 | *bp = 0; |
766 | pers->homephone = phone(buffer, bp - buffer, alldigits); | |
2e61617d | 767 | } |
f5ebcf9e EW |
768 | if (pers->loggedin) |
769 | findidle(pers); | |
770 | else | |
771 | findwhen(pers); | |
2e61617d BJ |
772 | } |
773 | ||
f5ebcf9e EW |
774 | /* |
775 | * find the last log in of a user by checking the LASTLOG file. | |
776 | * the entry is indexed by the uid, so this can only be done if | |
777 | * the uid is known (which it isn't in quick mode) | |
2e61617d BJ |
778 | */ |
779 | ||
780 | fwopen() | |
781 | { | |
f5ebcf9e EW |
782 | if ((lf = open(LASTLOG, 0)) < 0) |
783 | fprintf(stderr, "finger: %s open error\n", LASTLOG); | |
2e61617d BJ |
784 | } |
785 | ||
f5ebcf9e EW |
786 | findwhen(pers) |
787 | register struct person *pers; | |
2e61617d | 788 | { |
f5ebcf9e EW |
789 | struct lastlog ll; |
790 | int i; | |
791 | ||
792 | if (lf >= 0) { | |
793 | lseek(lf, (long)pers->pwd->pw_uid * sizeof ll, 0); | |
794 | if ((i = read(lf, (char *)&ll, sizeof ll)) == sizeof ll) { | |
795 | bcopy(ll.ll_line, pers->tty, LMAX); | |
796 | pers->tty[LMAX] = 0; | |
797 | bcopy(ll.ll_host, pers->host, HMAX); | |
798 | pers->host[HMAX] = 0; | |
799 | pers->loginat = ll.ll_time; | |
800 | } else { | |
801 | if (i != 0) | |
802 | fprintf(stderr, "finger: %s read error\n", | |
803 | LASTLOG); | |
804 | pers->tty[0] = 0; | |
805 | pers->host[0] = 0; | |
806 | pers->loginat = 0L; | |
807 | } | |
808 | } else { | |
809 | pers->tty[0] = 0; | |
810 | pers->host[0] = 0; | |
2e61617d | 811 | pers->loginat = 0L; |
2e61617d BJ |
812 | } |
813 | } | |
814 | ||
2e61617d BJ |
815 | fwclose() |
816 | { | |
f5ebcf9e EW |
817 | if (lf >= 0) |
818 | close(lf); | |
2e61617d BJ |
819 | } |
820 | ||
f5ebcf9e EW |
821 | /* |
822 | * find the idle time of a user by doing a stat on /dev/tty??, | |
823 | * where tty?? has been gotten from USERLOG, supposedly. | |
2e61617d | 824 | */ |
f5ebcf9e EW |
825 | findidle(pers) |
826 | register struct person *pers; | |
2e61617d | 827 | { |
f5ebcf9e EW |
828 | struct stat ttystatus; |
829 | static char buffer[20] = "/dev/"; | |
830 | long t; | |
831 | #define TTYLEN 5 | |
832 | ||
833 | strcpy(buffer + TTYLEN, pers->tty); | |
834 | buffer[TTYLEN+LMAX] = 0; | |
835 | if (stat(buffer, &ttystatus) < 0) { | |
836 | fprintf(stderr, "finger: Can't stat %s\n", buffer); | |
837 | exit(4); | |
2e61617d | 838 | } |
f5ebcf9e EW |
839 | time(&t); |
840 | if (t < ttystatus.st_atime) | |
841 | pers->idletime = 0L; | |
842 | else | |
843 | pers->idletime = t - ttystatus.st_atime; | |
844 | pers->writable = (ttystatus.st_mode & TALKABLE) == TALKABLE; | |
2e61617d BJ |
845 | } |
846 | ||
f5ebcf9e EW |
847 | /* |
848 | * print idle time in short format; this program always prints 4 characters; | |
849 | * if the idle time is zero, it prints 4 blanks. | |
2e61617d | 850 | */ |
f5ebcf9e EW |
851 | stimeprint(dt) |
852 | long *dt; | |
2e61617d | 853 | { |
f5ebcf9e EW |
854 | register struct tm *delta; |
855 | ||
856 | delta = gmtime(dt); | |
857 | if (delta->tm_yday == 0) | |
858 | if (delta->tm_hour == 0) | |
859 | if (delta->tm_min == 0) | |
860 | printf(" "); | |
861 | else | |
862 | printf(" %2d", delta->tm_min); | |
863 | else | |
864 | if (delta->tm_hour >= 10) | |
865 | printf("%3d:", delta->tm_hour); | |
866 | else | |
867 | printf("%1d:%02d", | |
868 | delta->tm_hour, delta->tm_min); | |
869 | else | |
870 | printf("%3dd", delta->tm_yday); | |
2e61617d BJ |
871 | } |
872 | ||
f5ebcf9e EW |
873 | /* |
874 | * print idle time in long format with care being taken not to pluralize | |
875 | * 1 minutes or 1 hours or 1 days. | |
876 | * print "prefix" first. | |
2e61617d | 877 | */ |
f5ebcf9e EW |
878 | ltimeprint(before, dt, after) |
879 | long *dt; | |
880 | char *before, *after; | |
2e61617d | 881 | { |
f5ebcf9e | 882 | register struct tm *delta; |
2e61617d | 883 | |
f5ebcf9e EW |
884 | delta = gmtime(dt); |
885 | if (delta->tm_yday == 0 && delta->tm_hour == 0 && delta->tm_min == 0 && | |
886 | delta->tm_sec <= 10) | |
887 | return (0); | |
888 | printf("%s", before); | |
889 | if (delta->tm_yday >= 10) | |
890 | printf("%d days", delta->tm_yday); | |
891 | else if (delta->tm_yday > 0) | |
892 | printf("%d day%s %d hour%s", | |
893 | delta->tm_yday, delta->tm_yday == 1 ? "" : "s", | |
894 | delta->tm_hour, delta->tm_hour == 1 ? "" : "s"); | |
895 | else | |
896 | if (delta->tm_hour >= 10) | |
897 | printf("%d hours", delta->tm_hour); | |
898 | else if (delta->tm_hour > 0) | |
899 | printf("%d hour%s %d minute%s", | |
900 | delta->tm_hour, delta->tm_hour == 1 ? "" : "s", | |
901 | delta->tm_min, delta->tm_min == 1 ? "" : "s"); | |
902 | else | |
903 | if (delta->tm_min >= 10) | |
904 | printf("%2d minutes", delta->tm_min); | |
905 | else if (delta->tm_min == 0) | |
906 | printf("%2d seconds", delta->tm_sec); | |
907 | else | |
908 | printf("%d minute%s %d second%s", | |
909 | delta->tm_min, | |
910 | delta->tm_min == 1 ? "" : "s", | |
911 | delta->tm_sec, | |
912 | delta->tm_sec == 1 ? "" : "s"); | |
913 | printf("%s", after); | |
2e61617d BJ |
914 | } |
915 | ||
f5ebcf9e EW |
916 | matchcmp(gname, login, given) |
917 | register char *gname; | |
918 | char *login; | |
919 | char *given; | |
2e61617d | 920 | { |
f5ebcf9e EW |
921 | char buffer[100]; |
922 | register char *bp, *lp; | |
923 | register c; | |
924 | ||
925 | if (*gname == ASTERISK) | |
926 | gname++; | |
927 | lp = 0; | |
928 | bp = buffer; | |
929 | for (;;) | |
930 | switch (c = *gname++) { | |
931 | case SAMENAME: | |
932 | for (lp = login; bp < buffer + sizeof buffer | |
933 | && (*bp++ = *lp++);) | |
934 | ; | |
935 | bp--; | |
936 | break; | |
937 | case ' ': | |
938 | case COMMA: | |
939 | case '\0': | |
940 | *bp = 0; | |
941 | if (namecmp(buffer, given)) | |
942 | return (1); | |
943 | if (c == COMMA || c == 0) | |
944 | return (0); | |
945 | bp = buffer; | |
946 | break; | |
947 | default: | |
948 | if (bp < buffer + sizeof buffer) | |
949 | *bp++ = c; | |
2e61617d | 950 | } |
f5ebcf9e | 951 | /*NOTREACHED*/ |
2e61617d BJ |
952 | } |
953 | ||
f5ebcf9e EW |
954 | namecmp(name1, name2) |
955 | register char *name1, *name2; | |
2e61617d | 956 | { |
f5ebcf9e EW |
957 | register c1, c2; |
958 | ||
959 | for (;;) { | |
960 | c1 = *name1++; | |
961 | if (islower(c1)) | |
962 | c1 = toupper(c1); | |
963 | c2 = *name2++; | |
964 | if (islower(c2)) | |
965 | c2 = toupper(c2); | |
966 | if (c1 != c2) | |
967 | break; | |
968 | if (c1 == 0) | |
969 | return (1); | |
2e61617d | 970 | } |
f5ebcf9e EW |
971 | if (!c1) { |
972 | for (name2--; isdigit(*name2); name2++) | |
973 | ; | |
974 | if (*name2 == 0) | |
975 | return (1); | |
976 | } else if (!c2) { | |
977 | for (name1--; isdigit(*name1); name1++) | |
978 | ; | |
979 | if (*name2 == 0) | |
980 | return (1); | |
2e61617d | 981 | } |
f5ebcf9e | 982 | return (0); |
2e61617d | 983 | } |
b474790b SL |
984 | |
985 | netfinger(name) | |
f5ebcf9e | 986 | char *name; |
b474790b SL |
987 | { |
988 | char *host; | |
989 | char fname[100]; | |
990 | struct hostent *hp; | |
991 | struct servent *sp; | |
f5ebcf9e | 992 | struct sockaddr_in sin; |
b474790b SL |
993 | int s; |
994 | char *rindex(); | |
995 | register FILE *f; | |
996 | register int c; | |
997 | register int lastc; | |
998 | ||
999 | if (name == NULL) | |
f5ebcf9e | 1000 | return (0); |
b474790b SL |
1001 | host = rindex(name, '@'); |
1002 | if (host == NULL) | |
f5ebcf9e | 1003 | return (0); |
b474790b SL |
1004 | *host++ = 0; |
1005 | hp = gethostbyname(host); | |
1006 | if (hp == NULL) { | |
1007 | static struct hostent def; | |
1008 | static struct in_addr defaddr; | |
1009 | static char namebuf[128]; | |
1010 | int inet_addr(); | |
1011 | ||
1012 | defaddr.s_addr = inet_addr(host); | |
1013 | if (defaddr.s_addr == -1) { | |
1014 | printf("unknown host: %s\n", host); | |
f5ebcf9e | 1015 | return (1); |
b474790b SL |
1016 | } |
1017 | strcpy(namebuf, host); | |
1018 | def.h_name = namebuf; | |
1019 | def.h_addr = (char *)&defaddr; | |
1020 | def.h_length = sizeof (struct in_addr); | |
1021 | def.h_addrtype = AF_INET; | |
1022 | def.h_aliases = 0; | |
1023 | hp = &def; | |
1024 | } | |
1025 | printf("[%s]", hp->h_name); | |
1026 | sp = getservbyname("finger", "tcp"); | |
1027 | if (sp == 0) { | |
1028 | printf("tcp/finger: unknown service\n"); | |
f5ebcf9e | 1029 | return (1); |
b474790b SL |
1030 | } |
1031 | sin.sin_family = hp->h_addrtype; | |
1032 | bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length); | |
1033 | sin.sin_port = sp->s_port; | |
1034 | s = socket(hp->h_addrtype, SOCK_STREAM, 0); | |
1035 | if (s < 0) { | |
1036 | fflush(stdout); | |
1037 | perror("socket"); | |
f5ebcf9e | 1038 | return (1); |
b474790b SL |
1039 | } |
1040 | if (connect(s, (char *)&sin, sizeof (sin)) < 0) { | |
1041 | fflush(stdout); | |
1042 | perror("connect"); | |
1043 | close(s); | |
f5ebcf9e | 1044 | return (1); |
b474790b SL |
1045 | } |
1046 | printf("\n"); | |
1047 | if (large) write(s, "/W ", 3); | |
1048 | write(s, name, strlen(name)); | |
1049 | write(s, "\r\n", 2); | |
1050 | f = fdopen(s, "r"); | |
1051 | while ((c = getc(f)) != EOF) { | |
1052 | switch(c) { | |
1053 | case 0210: | |
1054 | case 0211: | |
1055 | case 0212: | |
1056 | case 0214: | |
1057 | c -= 0200; | |
1058 | break; | |
1059 | case 0215: | |
1060 | c = '\n'; | |
1061 | break; | |
1062 | } | |
1063 | putchar(lastc = c); | |
1064 | } | |
1065 | if (lastc != '\n') | |
1066 | putchar('\n'); | |
a243bfdf | 1067 | (void)fclose(f); |
f5ebcf9e | 1068 | return (1); |
b474790b | 1069 | } |