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