Add spl's around queue manipulation
[unix-history] / usr / src / usr.bin / passwd / chfn.sh
CommitLineData
9e2ead69 1#ifndef lint
01eaadf6 2static char *sccsid = "@(#)chfn.sh 4.8 (Berkeley) %G%";
9e2ead69
KM
3#endif lint
4
64504187 5/*
9e2ead69 6 * changefinger - change finger entries
64504187
BJ
7 */
8#include <stdio.h>
9#include <signal.h>
10#include <pwd.h>
01eaadf6 11#include <ndbm.h>
840fc587
SL
12#include <sys/time.h>
13#include <sys/resource.h>
9f52e30a 14#include <sys/file.h>
f1ad9148
KM
15#include <ctype.h>
16
17struct default_values {
18 char *name;
19 char *office_num;
20 char *office_phone;
21 char *home_phone;
22};
64504187 23
a7da04e0 24char temp[] = "/etc/ptmp";
64504187 25char passwd[] = "/etc/passwd";
64504187 26struct passwd *pwd;
64504187
BJ
27char *crypt();
28char *getpass();
64504187
BJ
29
30main(argc, argv)
9e2ead69
KM
31 int argc;
32 char *argv[];
64504187 33{
9e2ead69 34 int user_uid;
f1ad9148 35 char replacement[4*BUFSIZ];
9f52e30a 36 int fd;
64504187 37 FILE *tf;
01eaadf6 38 DBM *dp;
64504187 39
9e2ead69
KM
40 if (argc > 2) {
41 printf("Usage: changefinger [user]\n");
64504187
BJ
42 exit(1);
43 }
9e2ead69
KM
44 /*
45 * Error check to make sure the user (foolishly) typed their own name.
46 */
47 user_uid = getuid();
48 if ((argc == 2) && (user_uid != 0)) {
49 pwd = getpwnam(argv[1]);
50 if (pwd == NULL) {
51 printf("%s%s%s%s%s%s%s%s",
52 "There is no account for ", argv[1],
53 " on this machine.\n",
54 "You probably mispelled your login name;\n",
55 "only root is allowed to change another",
56 " person's finger entry.\n",
57 "Note: you do not need to type your login",
58 " name as an argument.\n");
59 exit(1);
64504187 60 }
9e2ead69
KM
61 if (pwd->pw_uid != user_uid) {
62 printf("%s%s",
63 "You are not allowed to change another",
f1ad9148 64 " person's finger entry.\n");
9e2ead69
KM
65 exit(1);
66 }
67 }
68 /*
69 * If root is changing a finger entry, then find the uid that
70 * corresponds to the user's login name.
71 */
72 if ((argc == 2) && (user_uid == 0)) {
73 pwd = getpwnam(argv[1]);
74 if (pwd == NULL) {
75 printf("There is no account for %s on this machine\n",
76 pwd->pw_name);
77 exit(1);
78 }
79 user_uid = pwd->pw_uid;
80 }
f1ad9148
KM
81 if (argc == 1) {
82 pwd = getpwuid(user_uid);
83 if (pwd == NULL) {
84 fprintf(stderr, "No passwd file entry!?\n");
85 exit(1);
86 }
87 }
9e2ead69
KM
88 /*
89 * Collect name, room number, school phone, and home phone.
90 */
f1ad9148
KM
91 get_info(pwd->pw_gecos, replacement);
92
9e2ead69
KM
93 (void) signal(SIGHUP, SIG_IGN);
94 (void) signal(SIGINT, SIG_IGN);
95 (void) signal(SIGQUIT, SIG_IGN);
96 (void) signal(SIGTSTP, SIG_IGN);
fb8f0ff2 97 (void) umask(0);
9f52e30a
SL
98 if ((fd = open(temp, O_CREAT|O_EXCL|O_RDWR, 0644)) < 0) {
99 printf("Temporary file busy -- try again\n");
9e2ead69 100 exit(1);
64504187 101 }
9f52e30a
SL
102 if ((tf = fdopen(fd, "w")) == NULL) {
103 printf("Absurd fdopen failure - seek help\n");
9e2ead69
KM
104 goto out;
105 }
01eaadf6
RC
106 if ((dp = ndbmopen(passwd, O_RDWR, 0644)) == NULL) {
107 fprintf(stderr, "Warning: dbminit failed: ");
108 perror(passwd);
109 } else if (flock(dp->db_dirf, LOCK_EX) < 0) {
110 perror("Warning: lock failed");
111 ndbmclose(dp);
112 dp = NULL;
113 }
9f52e30a
SL
114 unlimit(RLIMIT_CPU);
115 unlimit(RLIMIT_FSIZE);
9e2ead69 116 /*
9f52e30a
SL
117 * Copy passwd to temp, replacing matching lines
118 * with new gecos field.
9e2ead69 119 */
9f52e30a 120 while ((pwd = getpwent()) != NULL) {
01eaadf6 121 if (pwd->pw_uid == user_uid) {
9e2ead69 122 pwd->pw_gecos = replacement;
01eaadf6
RC
123 replace(dp, pwd);
124 }
64504187
BJ
125 fprintf(tf,"%s:%s:%d:%d:%s:%s:%s\n",
126 pwd->pw_name,
127 pwd->pw_passwd,
128 pwd->pw_uid,
129 pwd->pw_gid,
130 pwd->pw_gecos,
131 pwd->pw_dir,
132 pwd->pw_shell);
133 }
9e2ead69 134 (void) endpwent();
9f52e30a 135 (void) fclose(tf);
01eaadf6
RC
136 ndbmclose(dp);
137 if (rename(temp, passwd) < 0) {
a7da04e0 138 fprintf(stderr, "chfn: "), perror("rename");
01eaadf6
RC
139 out:
140 (void) unlink(temp);
141 exit(1);
142 }
143 exit(0);
9f52e30a
SL
144}
145
146unlimit(lim)
147{
148 struct rlimit rlim;
149
150 rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
151 (void) setrlimit(lim, &rlim);
9e2ead69 152}
f1ad9148 153
01eaadf6
RC
154/*
155 * Replace the password entry in the dbm data base with pwd.
156 */
157replace(dp, pwd)
158 DBM *dp;
159 struct passwd *pwd;
a7da04e0 160{
01eaadf6
RC
161 datum key, content;
162 register char *cp, *tp;
163 char buf[BUFSIZ];
a7da04e0 164
01eaadf6
RC
165 if (dp == NULL)
166 return;
167
168 cp = buf;
169#define COMPACT(e) tp = pwd->pw_/**/e; while (*cp++ = *tp++);
170 COMPACT(name);
171 COMPACT(passwd);
172 *(int *)cp = pwd->pw_uid; cp += sizeof (int);
173 *(int *)cp = pwd->pw_gid; cp += sizeof (int);
174 *(int *)cp = pwd->pw_quota; cp += sizeof (int);
175 COMPACT(comment);
176 COMPACT(gecos);
177 COMPACT(dir);
178 COMPACT(shell);
179 content.dptr = buf;
180 content.dsize = cp - buf;
181 key.dptr = pwd->pw_name;
182 key.dsize = strlen(pwd->pw_name);
183 dbmstore(dp, key, content, DB_REPLACE);
184 key.dptr = (char *)&pwd->pw_uid;
185 key.dsize = sizeof (int);
186 dbmstore(dp, key, content, DB_REPLACE);
a7da04e0
RC
187}
188
9e2ead69
KM
189/*
190 * Get name, room number, school phone, and home phone.
191 */
f1ad9148
KM
192get_info(gecos_field, answer)
193 char *gecos_field;
9e2ead69
KM
194 char *answer;
195{
196 char *strcpy(), *strcat();
f1ad9148
KM
197 char in_str[BUFSIZ];
198 struct default_values *defaults, *get_defaults();
9e2ead69 199
f1ad9148
KM
200 answer[0] = '\0';
201 defaults = get_defaults(gecos_field);
202 printf("Default values are printed inside of of '[]'.\n");
203 printf("To accept the default, type <return>.\n");
204 printf("To have a blank entry, type the word 'none'.\n");
9e2ead69
KM
205 /*
206 * Get name.
207 */
208 do {
f1ad9148
KM
209 printf("\nName [%s]: ", defaults->name);
210 (void) fgets(in_str, BUFSIZ, stdin);
211 if (special_case(in_str, defaults->name))
212 break;
9e2ead69
KM
213 } while (illegal_input(in_str));
214 (void) strcpy(answer, in_str);
215 /*
216 * Get room number.
217 */
218 do {
f1ad9148
KM
219 printf("Room number (Exs: 597E or 197C) [%s]: ",
220 defaults->office_num);
221 (void) fgets(in_str, BUFSIZ, stdin);
222 if (special_case(in_str, defaults->office_num))
223 break;
9e2ead69
KM
224 } while (illegal_input(in_str) || illegal_building(in_str));
225 (void) strcat(strcat(answer, ","), in_str);
226 /*
227 * Get office phone number.
f1ad9148 228 * Remove hyphens and 642, x2, or 2 prefixes if present.
9e2ead69
KM
229 */
230 do {
f1ad9148
KM
231 printf("Office Phone (Ex: 1632) [%s]: ",
232 defaults->office_phone);
233 (void) fgets(in_str, BUFSIZ, stdin);
234 if (special_case(in_str, defaults->office_phone))
235 break;
9e2ead69
KM
236 remove_hyphens(in_str);
237 if ((strlen(in_str) == 8) && (strcmpn(in_str, "642", 3) == 0))
238 (void) strcpy(in_str, in_str+3);
239 if ((strlen(in_str) == 7) && (strcmpn(in_str, "x2", 2) == 0))
240 (void) strcpy(in_str, in_str+2);
f1ad9148
KM
241 if ((strlen(in_str) == 6) && (in_str[0] == '2'))
242 (void) strcpy(in_str, in_str+1);
243 } while (illegal_input(in_str) || not_all_digits(in_str)
244 || wrong_length(in_str, 4));
9e2ead69
KM
245 (void) strcat(strcat(answer, ","), in_str);
246 /*
247 * Get home phone number.
248 * Remove hyphens if present.
249 */
250 do {
f1ad9148
KM
251 printf("Home Phone (Ex: 9875432) [%s]: ", defaults->home_phone);
252 (void) fgets(in_str, BUFSIZ, stdin);
253 if (special_case(in_str, defaults->home_phone))
254 break;
9e2ead69 255 remove_hyphens(in_str);
f1ad9148 256 } while (illegal_input(in_str) || not_all_digits(in_str));
9e2ead69
KM
257 (void) strcat(strcat(answer, ","), in_str);
258}
f1ad9148 259
9e2ead69
KM
260/*
261 * Prints an error message if a ':' or a newline is found in the string.
262 * A message is also printed if the input string is too long.
263 * The password file uses :'s as seperators, and are not allowed in the "gcos"
f1ad9148 264 * field. Newlines serve as delimiters between users in the password file,
9e2ead69
KM
265 * and so, those too, are checked for. (I don't think that it is possible to
266 * type them in, but better safe than sorry)
267 *
268 * Returns '1' if a colon or newline is found or the input line is too long.
269 */
270illegal_input(input_str)
271 char *input_str;
272{
273 char *index();
274 char *ptr;
275 int error_flag = 0;
276 int length = strlen(input_str);
277
278 if (index(input_str, ':')) {
279 printf("':' is not allowed.\n");
280 error_flag = 1;
281 }
282 if (input_str[length-1] != '\n') {
283 /* the newline and the '\0' eat up two characters */
284 printf("Maximum number of characters allowed is %d\n",
f1ad9148 285 BUFSIZ-2);
9e2ead69
KM
286 /* flush the rest of the input line */
287 while (getchar() != '\n')
288 /* void */;
289 error_flag = 1;
290 }
291 /*
292 * Delete newline by shortening string by 1.
293 */
294 input_str[length-1] = '\0';
295 /*
296 * Don't allow control characters, etc in input string.
297 */
298 for (ptr=input_str; *ptr != '\0'; ptr++) {
299 if ((int) *ptr < 040) {
300 printf("Control characters are not allowed.\n");
301 error_flag = 1;
302 break;
303 }
304 }
305 return(error_flag);
306}
307
308/*
309 * Removes '-'s from the input string.
310 */
311remove_hyphens(str)
312 char *str;
313{
314 char *hyphen, *index(), *strcpy();
315
316 while ((hyphen=index(str, '-')) != NULL) {
317 (void) strcpy(hyphen, hyphen+1);
318 }
319}
64504187 320
f1ad9148
KM
321/*
322 * Checks to see if 'str' contains only digits (0-9). If not, then
323 * an error message is printed and '1' is returned.
324 */
325not_all_digits(str)
326 char *str;
327{
328 char *ptr;
329
330 for (ptr=str; *ptr != '\0'; ++ptr) {
331 if (!isdigit(*ptr)) {
332 printf("Phone numbers can only contain digits.\n");
333 return(1);
334 }
335 }
336 return(0);
337}
338
9e2ead69
KM
339/*
340 * Returns 1 when the length of the input string is not zero or equal to n.
341 * Prints an error message in this case.
342 */
343wrong_length(str, n)
344 char *str;
345 int n;
346{
f1ad9148 347
9e2ead69
KM
348 if ((strlen(str) != 0) && (strlen(str) != n)) {
349 printf("The phone number should be %d digits long.\n", n);
350 return(1);
351 }
352 return(0);
353}
354
355/*
356 * Make sure that building is 'E' or 'C'.
357 * Error correction is done if building is 'e', 'c', "evans", or "cory".
358 * Correction changes "str".
359 * The finger program determines the building by looking at the last
360 * character. Currently, finger only allows that character to be 'E' or 'C'.
361 *
362 * Returns 1 if incorrect room format.
363 *
364 * Note: this function assumes that the newline has been removed from str.
365 */
366illegal_building(str)
367 char *str;
368{
369 int length = strlen(str);
370 char *last_ch, *ptr;
371
372 /*
373 * Zero length strings are acceptable input.
374 */
375 if (length == 0)
376 return(0);
377 /*
378 * Delete "vans" and "ory".
379 */
380 if (strcmpn(str+length-4, "vans", 4) == 0) {
381 length -= 4;
382 str[length] = '\0';
383 }
384 if (strcmpn(str+length-3, "ory", 3) == 0) {
385 length -= 3;
386 str[length] = '\0';
387 }
388 last_ch = str+length-1;
389 /*
390 * Now change e to E or c to C.
391 */
392 if (*last_ch == 'e')
393 *last_ch = 'E';
394 if (*last_ch == 'c')
395 *last_ch = 'C';
396 /*
397 * Delete any spaces before the E or C.
398 */
399 for (ptr=last_ch-1; ptr>str; ptr--) {
400 if (*ptr != ' ')
401 break;
402 }
403 (void) strcpy(ptr+1, last_ch);
404 /*
405 * Make sure building is evans or cory.
406 */
407 if ((*last_ch != 'E') && (*last_ch != 'C')) {
408 printf("%s%s%s",
409 "The finger program requires that your",
410 " office be in Cory or Evans.\n",
411 "Enter this as (for example) 597E or 197C.\n");
412 return(1);
413 }
414 return(0);
64504187 415}
f1ad9148
KM
416
417/* get_defaults picks apart "str" and returns a structure points.
418 * "str" contains up to 4 fields separated by commas.
419 * Any field that is missing is set to blank.
420 */
421struct default_values
422*get_defaults(str)
423 char *str;
424{
425 struct default_values *answer;
426 char *malloc(), *index();
427
428 answer = (struct default_values *)
429 malloc((unsigned)sizeof(struct default_values));
430 if (answer == (struct default_values *) NULL) {
431 fprintf(stderr,
432 "\nUnable to allocate storage in get_defaults!\n");
433 exit(1);
434 }
435 /*
436 * Values if no corresponding string in "str".
437 */
438 answer->name = str;
439 answer->office_num = "";
440 answer->office_phone = "";
441 answer->home_phone = "";
442 str = index(answer->name, ',');
443 if (str == 0)
444 return(answer);
445 *str = '\0';
446 answer->office_num = str + 1;
447 str = index(answer->office_num, ',');
448 if (str == 0)
449 return(answer);
450 *str = '\0';
451 answer->office_phone = str + 1;
452 str = index(answer->office_phone, ',');
453 if (str == 0)
454 return(answer);
455 *str = '\0';
456 answer->home_phone = str + 1;
457 return(answer);
458}
459
460/*
461 * special_case returns true when either the default is accepted
462 * (str = '\n'), or when 'none' is typed. 'none' is accepted in
463 * either upper or lower case (or any combination). 'str' is modified
464 * in these two cases.
465 */
466int special_case(str,default_str)
467 char *str;
468 char *default_str;
469{
470 static char word[] = "none\n";
471 char *ptr, *wordptr;
472
473 /*
474 * If the default is accepted, then change the old string do the
475 * default string.
476 */
477 if (*str == '\n') {
478 (void) strcpy(str, default_str);
479 return(1);
480 }
481 /*
482 * Check to see if str is 'none'. (It is questionable if case
483 * insensitivity is worth the hair).
484 */
485 wordptr = word-1;
486 for (ptr=str; *ptr != '\0'; ++ptr) {
487 ++wordptr;
488 if (*wordptr == '\0') /* then words are different sizes */
489 return(0);
490 if (*ptr == *wordptr)
491 continue;
492 if (isupper(*ptr) && (tolower(*ptr) == *wordptr))
493 continue;
494 /*
495 * At this point we have a mismatch, so we return
496 */
497 return(0);
498 }
499 /*
500 * Make sure that words are the same length.
501 */
502 if (*(wordptr+1) != '\0')
503 return(0);
504 /*
505 * Change 'str' to be the null string
506 */
507 *str = '\0';
508 return(1);
509}