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 |
1d3c77a8 | 14 | static char sccsid[] = "@(#)finger.c 5.8 (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 */ | |
1d3c77a8 | 74 | #define TALKABLE 0220 /* tty is writable if 220 mode */ |
f5ebcf9e EW |
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; | |
9bcd7031 JB |
406 | if (isprint(c) || isspace(c)) |
407 | putchar(c); | |
408 | else | |
409 | putchar(c ^ 100); | |
f5ebcf9e EW |
410 | } |
411 | fclose(fp); | |
412 | putchar('\n'); | |
2e61617d | 413 | } |
f5ebcf9e | 414 | free(s); |
2e61617d | 415 | } |
f5ebcf9e EW |
416 | if (plan) { |
417 | s = malloc(strlen(p->pwd->pw_dir) + | |
418 | sizeof PLAN); | |
419 | strcpy(s, p->pwd->pw_dir); | |
420 | strcat(s, PLAN); | |
421 | if ((fp = fopen(s, "r")) == 0) | |
422 | printf("No Plan.\n"); | |
423 | else { | |
424 | printf("Plan:\n"); | |
425 | while ((c = getc(fp)) != EOF) | |
9bcd7031 JB |
426 | if (isprint(c) || isspace(c)) |
427 | putchar(c); | |
428 | else | |
429 | putchar(c ^ 100); | |
f5ebcf9e | 430 | fclose(fp); |
2e61617d | 431 | } |
f5ebcf9e | 432 | free(s); |
2e61617d | 433 | } |
2e61617d | 434 | } |
f5ebcf9e EW |
435 | if (p->link != 0) |
436 | putchar('\n'); | |
437 | } | |
2e61617d BJ |
438 | } |
439 | ||
f5ebcf9e EW |
440 | /* |
441 | * Duplicate a pwd entry. | |
442 | * Note: Only the useful things (what the program currently uses) are copied. | |
2e61617d | 443 | */ |
f5ebcf9e EW |
444 | struct passwd * |
445 | pwdcopy(pfrom) | |
446 | register struct passwd *pfrom; | |
2e61617d | 447 | { |
f5ebcf9e EW |
448 | register struct passwd *pto; |
449 | ||
450 | pto = (struct passwd *) malloc(sizeof *pto); | |
451 | #define savestr(s) strcpy(malloc(strlen(s) + 1), s) | |
452 | pto->pw_name = savestr(pfrom->pw_name); | |
2e61617d | 453 | pto->pw_uid = pfrom->pw_uid; |
f5ebcf9e EW |
454 | pto->pw_gecos = savestr(pfrom->pw_gecos); |
455 | pto->pw_dir = savestr(pfrom->pw_dir); | |
456 | pto->pw_shell = savestr(pfrom->pw_shell); | |
457 | #undef savestr | |
458 | return pto; | |
2e61617d BJ |
459 | } |
460 | ||
f5ebcf9e EW |
461 | /* |
462 | * print out information on quick format giving just name, tty, login time | |
463 | * and idle time if idle is set. | |
2e61617d | 464 | */ |
f5ebcf9e EW |
465 | quickprint(pers) |
466 | register struct person *pers; | |
2e61617d | 467 | { |
f5ebcf9e EW |
468 | printf("%-*.*s ", NMAX, NMAX, pers->name); |
469 | if (pers->loggedin) { | |
470 | if (idle) { | |
471 | findidle(pers); | |
472 | printf("%c%-*s %-16.16s", pers->writable ? ' ' : '*', | |
473 | LMAX, pers->tty, ctime(&pers->loginat)); | |
474 | ltimeprint(" ", &pers->idletime, ""); | |
475 | } else | |
476 | printf(" %-*s %-16.16s", LMAX, | |
477 | pers->tty, ctime(&pers->loginat)); | |
478 | putchar('\n'); | |
479 | } else | |
480 | printf(" Not Logged In\n"); | |
2e61617d BJ |
481 | } |
482 | ||
f5ebcf9e EW |
483 | /* |
484 | * print out information in short format, giving login name, full name, | |
485 | * tty, idle time, login time, office location and phone. | |
2e61617d | 486 | */ |
f5ebcf9e EW |
487 | shortprint(pers) |
488 | register struct person *pers; | |
2e61617d | 489 | { |
f5ebcf9e EW |
490 | char *p; |
491 | char dialup; | |
2e61617d | 492 | |
f5ebcf9e EW |
493 | if (pers->pwd == 0) { |
494 | printf("%-15s ???\n", pers->name); | |
495 | return; | |
2e61617d | 496 | } |
f5ebcf9e | 497 | printf("%-*s", NMAX, pers->pwd->pw_name); |
2e61617d | 498 | dialup = 0; |
f5ebcf9e EW |
499 | if (wide) { |
500 | if (pers->realname) | |
501 | printf(" %-20.20s", pers->realname); | |
502 | else | |
503 | printf(" ??? "); | |
2e61617d | 504 | } |
f5ebcf9e EW |
505 | putchar(' '); |
506 | if (pers->loggedin && !pers->writable) | |
507 | putchar('*'); | |
508 | else | |
509 | putchar(' '); | |
510 | if (*pers->tty) { | |
511 | if (pers->tty[0] == 't' && pers->tty[1] == 't' && | |
512 | pers->tty[2] == 'y') { | |
513 | if (pers->tty[3] == 'd' && pers->loggedin) | |
514 | dialup = 1; | |
515 | printf("%-2.2s ", pers->tty + 3); | |
516 | } else | |
517 | printf("%-2.2s ", pers->tty); | |
518 | } else | |
519 | printf(" "); | |
520 | p = ctime(&pers->loginat); | |
521 | if (pers->loggedin) { | |
522 | stimeprint(&pers->idletime); | |
523 | printf(" %3.3s %-5.5s ", p, p + 11); | |
524 | } else if (pers->loginat == 0) | |
525 | printf(" < . . . . >"); | |
27cee9bc | 526 | else if (tloc - pers->loginat >= 180 * 24 * 60 * 60) |
f5ebcf9e | 527 | printf(" <%-6.6s, %-4.4s>", p + 4, p + 20); |
27cee9bc | 528 | else |
f5ebcf9e EW |
529 | printf(" <%-12.12s>", p + 4); |
530 | if (dialup && pers->homephone) | |
531 | printf(" %20s", pers->homephone); | |
532 | else { | |
533 | if (pers->office) | |
534 | printf(" %-11.11s", pers->office); | |
535 | else if (pers->officephone || pers->homephone) | |
536 | printf(" "); | |
537 | if (pers->officephone) | |
538 | printf(" %s", pers->officephone); | |
539 | else if (pers->homephone) | |
540 | printf(" %s", pers->homephone); | |
2e61617d | 541 | } |
f5ebcf9e | 542 | putchar('\n'); |
2e61617d BJ |
543 | } |
544 | ||
f5ebcf9e EW |
545 | /* |
546 | * print out a person in long format giving all possible information. | |
547 | * directory and shell are inhibited if unbrief is clear. | |
2e61617d | 548 | */ |
f5ebcf9e EW |
549 | personprint(pers) |
550 | register struct person *pers; | |
2e61617d | 551 | { |
f5ebcf9e EW |
552 | if (pers->pwd == 0) { |
553 | printf("Login name: %-10s\t\t\tIn real life: ???\n", | |
554 | pers->name); | |
555 | return; | |
2e61617d | 556 | } |
f5ebcf9e EW |
557 | printf("Login name: %-10s", pers->pwd->pw_name); |
558 | if (pers->loggedin && !pers->writable) | |
559 | printf(" (messages off) "); | |
560 | else | |
561 | printf(" "); | |
562 | if (pers->realname) | |
563 | printf("In real life: %s", pers->realname); | |
564 | if (pers->office) { | |
565 | printf("\nOffice: %-.11s", pers->office); | |
566 | if (pers->officephone) { | |
567 | printf(", %s", pers->officephone); | |
568 | if (pers->homephone) | |
569 | printf("\t\tHome phone: %s", pers->homephone); | |
570 | else if (pers->random) | |
571 | printf("\t\t%s", pers->random); | |
572 | } else | |
573 | if (pers->homephone) | |
574 | printf("\t\t\tHome phone: %s", pers->homephone); | |
575 | else if (pers->random) | |
576 | printf("\t\t\t%s", pers->random); | |
577 | } else if (pers->officephone) { | |
578 | printf("\nPhone: %s", pers->officephone); | |
579 | if (pers->homephone) | |
580 | printf(", %s", pers->homephone); | |
581 | if (pers->random) | |
582 | printf(", %s", pers->random); | |
583 | } else if (pers->homephone) { | |
584 | printf("\nPhone: %s", pers->homephone); | |
585 | if (pers->random) | |
586 | printf(", %s", pers->random); | |
587 | } else if (pers->random) | |
588 | printf("\n%s", pers->random); | |
589 | if (unbrief) { | |
590 | printf("\nDirectory: %-25s", pers->pwd->pw_dir); | |
591 | if (*pers->pwd->pw_shell) | |
592 | printf("\tShell: %-s", pers->pwd->pw_shell); | |
2e61617d | 593 | } |
f5ebcf9e EW |
594 | if (pers->loggedin) { |
595 | register char *ep = ctime(&pers->loginat); | |
596 | if (*pers->host) { | |
597 | printf("\nOn since %15.15s on %s from %s", | |
598 | &ep[4], pers->tty, pers->host); | |
599 | ltimeprint("\n", &pers->idletime, " Idle Time"); | |
600 | } else { | |
601 | printf("\nOn since %15.15s on %-*s", | |
602 | &ep[4], LMAX, pers->tty); | |
603 | ltimeprint("\t", &pers->idletime, " Idle Time"); | |
2e61617d | 604 | } |
f5ebcf9e EW |
605 | } else if (pers->loginat == 0) |
606 | printf("\nNever logged in."); | |
27cee9bc | 607 | else if (tloc - pers->loginat > 180 * 24 * 60 * 60) { |
f5ebcf9e EW |
608 | register char *ep = ctime(&pers->loginat); |
609 | printf("\nLast login %10.10s, %4.4s on %s", | |
610 | ep, ep+20, pers->tty); | |
611 | if (*pers->host) | |
612 | printf(" from %s", pers->host); | |
613 | } else { | |
614 | register char *ep = ctime(&pers->loginat); | |
615 | printf("\nLast login %16.16s on %s", ep, pers->tty); | |
616 | if (*pers->host) | |
617 | printf(" from %s", pers->host); | |
27cee9bc | 618 | } |
f5ebcf9e | 619 | putchar('\n'); |
2e61617d BJ |
620 | } |
621 | ||
2e61617d BJ |
622 | /* |
623 | * very hacky section of code to format phone numbers. filled with | |
624 | * magic constants like 4, 7 and 10. | |
625 | */ | |
f5ebcf9e EW |
626 | char * |
627 | phone(s, len, alldigits) | |
628 | register char *s; | |
629 | int len; | |
630 | char alldigits; | |
2e61617d | 631 | { |
f5ebcf9e EW |
632 | char fonebuf[15]; |
633 | register char *p = fonebuf; | |
634 | register i; | |
635 | ||
636 | if (!alldigits) | |
637 | return (strcpy(malloc(len + 1), s)); | |
638 | switch (len) { | |
639 | case 4: | |
640 | *p++ = ' '; | |
641 | *p++ = 'x'; | |
642 | *p++ = '2'; | |
643 | *p++ = '-'; | |
644 | for (i = 0; i < 4; i++) | |
645 | *p++ = *s++; | |
2e61617d | 646 | break; |
172389f0 JB |
647 | case 5: |
648 | *p++ = ' '; | |
649 | *p++ = 'x'; | |
650 | *p++ = *s++; | |
651 | *p++ = '-'; | |
652 | for (i = 0; i < 4; i++) | |
653 | *p++ = *s++; | |
654 | break; | |
f5ebcf9e EW |
655 | case 7: |
656 | for (i = 0; i < 3; i++) | |
657 | *p++ = *s++; | |
658 | *p++ = '-'; | |
659 | for (i = 0; i < 4; i++) | |
660 | *p++ = *s++; | |
2e61617d | 661 | break; |
f5ebcf9e EW |
662 | case 10: |
663 | for (i = 0; i < 3; i++) | |
664 | *p++ = *s++; | |
665 | *p++ = '-'; | |
666 | for (i = 0; i < 3; i++) | |
667 | *p++ = *s++; | |
668 | *p++ = '-'; | |
669 | for (i = 0; i < 4; i++) | |
670 | *p++ = *s++; | |
2e61617d | 671 | break; |
f5ebcf9e EW |
672 | case 0: |
673 | return 0; | |
674 | default: | |
675 | return (strcpy(malloc(len + 1), s)); | |
2e61617d | 676 | } |
f5ebcf9e EW |
677 | *p++ = 0; |
678 | return (strcpy(malloc(p - fonebuf), fonebuf)); | |
2e61617d BJ |
679 | } |
680 | ||
f5ebcf9e EW |
681 | /* |
682 | * decode the information in the gecos field of /etc/passwd | |
2e61617d | 683 | */ |
f5ebcf9e EW |
684 | decode(pers) |
685 | register struct person *pers; | |
2e61617d | 686 | { |
f5ebcf9e EW |
687 | char buffer[256]; |
688 | register char *bp, *gp, *lp; | |
689 | int alldigits; | |
690 | int hasspace; | |
691 | int len; | |
692 | ||
693 | pers->realname = 0; | |
694 | pers->office = 0; | |
695 | pers->officephone = 0; | |
696 | pers->homephone = 0; | |
697 | pers->random = 0; | |
698 | if (pers->pwd == 0) | |
699 | return; | |
700 | gp = pers->pwd->pw_gecos; | |
701 | bp = buffer; | |
702 | if (*gp == ASTERISK) | |
2e61617d | 703 | gp++; |
f5ebcf9e EW |
704 | while (*gp && *gp != COMMA) /* name */ |
705 | if (*gp == SAMENAME) { | |
706 | lp = pers->pwd->pw_name; | |
707 | if (islower(*lp)) | |
708 | *bp++ = toupper(*lp++); | |
709 | while (*bp++ = *lp++) | |
710 | ; | |
711 | bp--; | |
712 | gp++; | |
713 | } else | |
714 | *bp++ = *gp++; | |
715 | *bp++ = 0; | |
716 | if ((len = bp - buffer) > 1) | |
717 | pers->realname = strcpy(malloc(len), buffer); | |
718 | if (*gp == COMMA) { /* office */ | |
719 | gp++; | |
720 | hasspace = 0; | |
721 | bp = buffer; | |
722 | while (*gp && *gp != COMMA) { | |
723 | *bp = *gp++; | |
724 | if (*bp == ' ') | |
725 | hasspace = 1; | |
726 | /* leave 5 for Cory and Evans expansion */ | |
727 | if (bp < buffer + sizeof buffer - 6) | |
728 | bp++; | |
2e61617d | 729 | } |
f5ebcf9e EW |
730 | *bp = 0; |
731 | len = bp - buffer; | |
732 | bp--; /* point to last character */ | |
733 | if (hasspace || len == 0) | |
734 | len++; | |
735 | else if (*bp == CORY) { | |
736 | strcpy(bp, " Cory"); | |
737 | len += 5; | |
738 | } else if (*bp == EVANS) { | |
739 | strcpy(bp, " Evans"); | |
740 | len += 6; | |
741 | } else | |
742 | len++; | |
743 | if (len > 1) | |
744 | pers->office = strcpy(malloc(len), buffer); | |
745 | } | |
746 | if (*gp == COMMA) { /* office phone */ | |
2e61617d | 747 | gp++; |
f5ebcf9e | 748 | bp = buffer; |
2e61617d | 749 | alldigits = 1; |
f5ebcf9e EW |
750 | while (*gp && *gp != COMMA) { |
751 | *bp = *gp++; | |
752 | if (!isdigit(*bp)) | |
753 | alldigits = 0; | |
754 | if (bp < buffer + sizeof buffer - 1) | |
755 | bp++; | |
2e61617d | 756 | } |
f5ebcf9e EW |
757 | *bp = 0; |
758 | pers->officephone = phone(buffer, bp - buffer, alldigits); | |
759 | } | |
760 | if (*gp == COMMA) { /* home phone */ | |
761 | gp++; | |
762 | bp = buffer; | |
763 | alldigits = 1; | |
764 | while (*gp && *gp != COMMA) { | |
2e61617d | 765 | *bp = *gp++; |
f5ebcf9e EW |
766 | if (!isdigit(*bp)) |
767 | alldigits = 0; | |
768 | if (bp < buffer + sizeof buffer - 1) | |
2e61617d | 769 | bp++; |
2e61617d | 770 | } |
f5ebcf9e EW |
771 | *bp = 0; |
772 | pers->homephone = phone(buffer, bp - buffer, alldigits); | |
2e61617d | 773 | } |
f5ebcf9e EW |
774 | if (pers->loggedin) |
775 | findidle(pers); | |
776 | else | |
777 | findwhen(pers); | |
2e61617d BJ |
778 | } |
779 | ||
f5ebcf9e EW |
780 | /* |
781 | * find the last log in of a user by checking the LASTLOG file. | |
782 | * the entry is indexed by the uid, so this can only be done if | |
783 | * the uid is known (which it isn't in quick mode) | |
2e61617d BJ |
784 | */ |
785 | ||
786 | fwopen() | |
787 | { | |
f5ebcf9e EW |
788 | if ((lf = open(LASTLOG, 0)) < 0) |
789 | fprintf(stderr, "finger: %s open error\n", LASTLOG); | |
2e61617d BJ |
790 | } |
791 | ||
f5ebcf9e EW |
792 | findwhen(pers) |
793 | register struct person *pers; | |
2e61617d | 794 | { |
f5ebcf9e EW |
795 | struct lastlog ll; |
796 | int i; | |
797 | ||
798 | if (lf >= 0) { | |
799 | lseek(lf, (long)pers->pwd->pw_uid * sizeof ll, 0); | |
800 | if ((i = read(lf, (char *)&ll, sizeof ll)) == sizeof ll) { | |
801 | bcopy(ll.ll_line, pers->tty, LMAX); | |
802 | pers->tty[LMAX] = 0; | |
803 | bcopy(ll.ll_host, pers->host, HMAX); | |
804 | pers->host[HMAX] = 0; | |
805 | pers->loginat = ll.ll_time; | |
806 | } else { | |
807 | if (i != 0) | |
808 | fprintf(stderr, "finger: %s read error\n", | |
809 | LASTLOG); | |
810 | pers->tty[0] = 0; | |
811 | pers->host[0] = 0; | |
812 | pers->loginat = 0L; | |
813 | } | |
814 | } else { | |
815 | pers->tty[0] = 0; | |
816 | pers->host[0] = 0; | |
2e61617d | 817 | pers->loginat = 0L; |
2e61617d BJ |
818 | } |
819 | } | |
820 | ||
2e61617d BJ |
821 | fwclose() |
822 | { | |
f5ebcf9e EW |
823 | if (lf >= 0) |
824 | close(lf); | |
2e61617d BJ |
825 | } |
826 | ||
f5ebcf9e EW |
827 | /* |
828 | * find the idle time of a user by doing a stat on /dev/tty??, | |
829 | * where tty?? has been gotten from USERLOG, supposedly. | |
2e61617d | 830 | */ |
f5ebcf9e EW |
831 | findidle(pers) |
832 | register struct person *pers; | |
2e61617d | 833 | { |
f5ebcf9e EW |
834 | struct stat ttystatus; |
835 | static char buffer[20] = "/dev/"; | |
836 | long t; | |
837 | #define TTYLEN 5 | |
838 | ||
839 | strcpy(buffer + TTYLEN, pers->tty); | |
840 | buffer[TTYLEN+LMAX] = 0; | |
841 | if (stat(buffer, &ttystatus) < 0) { | |
842 | fprintf(stderr, "finger: Can't stat %s\n", buffer); | |
843 | exit(4); | |
2e61617d | 844 | } |
f5ebcf9e EW |
845 | time(&t); |
846 | if (t < ttystatus.st_atime) | |
847 | pers->idletime = 0L; | |
848 | else | |
849 | pers->idletime = t - ttystatus.st_atime; | |
850 | pers->writable = (ttystatus.st_mode & TALKABLE) == TALKABLE; | |
2e61617d BJ |
851 | } |
852 | ||
f5ebcf9e EW |
853 | /* |
854 | * print idle time in short format; this program always prints 4 characters; | |
855 | * if the idle time is zero, it prints 4 blanks. | |
2e61617d | 856 | */ |
f5ebcf9e EW |
857 | stimeprint(dt) |
858 | long *dt; | |
2e61617d | 859 | { |
f5ebcf9e EW |
860 | register struct tm *delta; |
861 | ||
862 | delta = gmtime(dt); | |
863 | if (delta->tm_yday == 0) | |
864 | if (delta->tm_hour == 0) | |
865 | if (delta->tm_min == 0) | |
866 | printf(" "); | |
867 | else | |
868 | printf(" %2d", delta->tm_min); | |
869 | else | |
870 | if (delta->tm_hour >= 10) | |
871 | printf("%3d:", delta->tm_hour); | |
872 | else | |
873 | printf("%1d:%02d", | |
874 | delta->tm_hour, delta->tm_min); | |
875 | else | |
876 | printf("%3dd", delta->tm_yday); | |
2e61617d BJ |
877 | } |
878 | ||
f5ebcf9e EW |
879 | /* |
880 | * print idle time in long format with care being taken not to pluralize | |
881 | * 1 minutes or 1 hours or 1 days. | |
882 | * print "prefix" first. | |
2e61617d | 883 | */ |
f5ebcf9e EW |
884 | ltimeprint(before, dt, after) |
885 | long *dt; | |
886 | char *before, *after; | |
2e61617d | 887 | { |
f5ebcf9e | 888 | register struct tm *delta; |
2e61617d | 889 | |
f5ebcf9e EW |
890 | delta = gmtime(dt); |
891 | if (delta->tm_yday == 0 && delta->tm_hour == 0 && delta->tm_min == 0 && | |
892 | delta->tm_sec <= 10) | |
893 | return (0); | |
894 | printf("%s", before); | |
895 | if (delta->tm_yday >= 10) | |
896 | printf("%d days", delta->tm_yday); | |
897 | else if (delta->tm_yday > 0) | |
898 | printf("%d day%s %d hour%s", | |
899 | delta->tm_yday, delta->tm_yday == 1 ? "" : "s", | |
900 | delta->tm_hour, delta->tm_hour == 1 ? "" : "s"); | |
901 | else | |
902 | if (delta->tm_hour >= 10) | |
903 | printf("%d hours", delta->tm_hour); | |
904 | else if (delta->tm_hour > 0) | |
905 | printf("%d hour%s %d minute%s", | |
906 | delta->tm_hour, delta->tm_hour == 1 ? "" : "s", | |
907 | delta->tm_min, delta->tm_min == 1 ? "" : "s"); | |
908 | else | |
909 | if (delta->tm_min >= 10) | |
910 | printf("%2d minutes", delta->tm_min); | |
911 | else if (delta->tm_min == 0) | |
912 | printf("%2d seconds", delta->tm_sec); | |
913 | else | |
914 | printf("%d minute%s %d second%s", | |
915 | delta->tm_min, | |
916 | delta->tm_min == 1 ? "" : "s", | |
917 | delta->tm_sec, | |
918 | delta->tm_sec == 1 ? "" : "s"); | |
919 | printf("%s", after); | |
2e61617d BJ |
920 | } |
921 | ||
f5ebcf9e EW |
922 | matchcmp(gname, login, given) |
923 | register char *gname; | |
924 | char *login; | |
925 | char *given; | |
2e61617d | 926 | { |
f5ebcf9e EW |
927 | char buffer[100]; |
928 | register char *bp, *lp; | |
929 | register c; | |
930 | ||
931 | if (*gname == ASTERISK) | |
932 | gname++; | |
933 | lp = 0; | |
934 | bp = buffer; | |
935 | for (;;) | |
936 | switch (c = *gname++) { | |
937 | case SAMENAME: | |
938 | for (lp = login; bp < buffer + sizeof buffer | |
939 | && (*bp++ = *lp++);) | |
940 | ; | |
941 | bp--; | |
942 | break; | |
943 | case ' ': | |
944 | case COMMA: | |
945 | case '\0': | |
946 | *bp = 0; | |
947 | if (namecmp(buffer, given)) | |
948 | return (1); | |
949 | if (c == COMMA || c == 0) | |
950 | return (0); | |
951 | bp = buffer; | |
952 | break; | |
953 | default: | |
954 | if (bp < buffer + sizeof buffer) | |
955 | *bp++ = c; | |
2e61617d | 956 | } |
f5ebcf9e | 957 | /*NOTREACHED*/ |
2e61617d BJ |
958 | } |
959 | ||
f5ebcf9e EW |
960 | namecmp(name1, name2) |
961 | register char *name1, *name2; | |
2e61617d | 962 | { |
f5ebcf9e EW |
963 | register c1, c2; |
964 | ||
965 | for (;;) { | |
966 | c1 = *name1++; | |
967 | if (islower(c1)) | |
968 | c1 = toupper(c1); | |
969 | c2 = *name2++; | |
970 | if (islower(c2)) | |
971 | c2 = toupper(c2); | |
972 | if (c1 != c2) | |
973 | break; | |
974 | if (c1 == 0) | |
975 | return (1); | |
2e61617d | 976 | } |
f5ebcf9e EW |
977 | if (!c1) { |
978 | for (name2--; isdigit(*name2); name2++) | |
979 | ; | |
980 | if (*name2 == 0) | |
981 | return (1); | |
982 | } else if (!c2) { | |
983 | for (name1--; isdigit(*name1); name1++) | |
984 | ; | |
985 | if (*name2 == 0) | |
986 | return (1); | |
2e61617d | 987 | } |
f5ebcf9e | 988 | return (0); |
2e61617d | 989 | } |
b474790b SL |
990 | |
991 | netfinger(name) | |
f5ebcf9e | 992 | char *name; |
b474790b SL |
993 | { |
994 | char *host; | |
995 | char fname[100]; | |
996 | struct hostent *hp; | |
997 | struct servent *sp; | |
f5ebcf9e | 998 | struct sockaddr_in sin; |
b474790b SL |
999 | int s; |
1000 | char *rindex(); | |
1001 | register FILE *f; | |
1002 | register int c; | |
1003 | register int lastc; | |
1004 | ||
1005 | if (name == NULL) | |
f5ebcf9e | 1006 | return (0); |
b474790b SL |
1007 | host = rindex(name, '@'); |
1008 | if (host == NULL) | |
f5ebcf9e | 1009 | return (0); |
b474790b SL |
1010 | *host++ = 0; |
1011 | hp = gethostbyname(host); | |
1012 | if (hp == NULL) { | |
1013 | static struct hostent def; | |
1014 | static struct in_addr defaddr; | |
b7884b64 | 1015 | static char *alist[1]; |
b474790b SL |
1016 | static char namebuf[128]; |
1017 | int inet_addr(); | |
1018 | ||
1019 | defaddr.s_addr = inet_addr(host); | |
1020 | if (defaddr.s_addr == -1) { | |
1021 | printf("unknown host: %s\n", host); | |
f5ebcf9e | 1022 | return (1); |
b474790b SL |
1023 | } |
1024 | strcpy(namebuf, host); | |
1025 | def.h_name = namebuf; | |
332ff47b | 1026 | def.h_addr_list = alist, def.h_addr = (char *)&defaddr; |
b474790b SL |
1027 | def.h_length = sizeof (struct in_addr); |
1028 | def.h_addrtype = AF_INET; | |
1029 | def.h_aliases = 0; | |
1030 | hp = &def; | |
1031 | } | |
1032 | printf("[%s]", hp->h_name); | |
1033 | sp = getservbyname("finger", "tcp"); | |
1034 | if (sp == 0) { | |
1035 | printf("tcp/finger: unknown service\n"); | |
f5ebcf9e | 1036 | return (1); |
b474790b SL |
1037 | } |
1038 | sin.sin_family = hp->h_addrtype; | |
1039 | bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length); | |
1040 | sin.sin_port = sp->s_port; | |
1041 | s = socket(hp->h_addrtype, SOCK_STREAM, 0); | |
1042 | if (s < 0) { | |
1043 | fflush(stdout); | |
1044 | perror("socket"); | |
f5ebcf9e | 1045 | return (1); |
b474790b SL |
1046 | } |
1047 | if (connect(s, (char *)&sin, sizeof (sin)) < 0) { | |
1048 | fflush(stdout); | |
1049 | perror("connect"); | |
1050 | close(s); | |
f5ebcf9e | 1051 | return (1); |
b474790b SL |
1052 | } |
1053 | printf("\n"); | |
1054 | if (large) write(s, "/W ", 3); | |
1055 | write(s, name, strlen(name)); | |
1056 | write(s, "\r\n", 2); | |
1057 | f = fdopen(s, "r"); | |
1058 | while ((c = getc(f)) != EOF) { | |
1059 | switch(c) { | |
1060 | case 0210: | |
1061 | case 0211: | |
1062 | case 0212: | |
1063 | case 0214: | |
1064 | c -= 0200; | |
1065 | break; | |
1066 | case 0215: | |
1067 | c = '\n'; | |
1068 | break; | |
1069 | } | |
9bcd7031 JB |
1070 | lastc = c; |
1071 | if (isprint(c) || isspace(c)) | |
1072 | putchar(c); | |
1073 | else | |
1074 | putchar(c ^ 100); | |
b474790b SL |
1075 | } |
1076 | if (lastc != '\n') | |
1077 | putchar('\n'); | |
a243bfdf | 1078 | (void)fclose(f); |
f5ebcf9e | 1079 | return (1); |
b474790b | 1080 | } |