1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1991, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/param.h>
33 #include <sys/endian.h>
34 #include <sys/stat.h>
35 #include <arpa/inet.h>
36
37 #include <db.h>
38 #include <err.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <libgen.h>
42 #include <limits.h>
43 #include <pwd.h>
44 #include <signal.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49
50 #include "pw_scan.h"
51
52 #define INSECURE 1
53 #define SECURE 2
54 #define PERM_INSECURE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
55 #define PERM_SECURE (S_IRUSR|S_IWUSR)
56 #define LEGACY_VERSION(x) _PW_VERSIONED(x, 3)
57 #define CURRENT_VERSION(x) _PW_VERSIONED(x, 4)
58
59 static HASHINFO openinfo = {
60 4096, /* bsize */
61 32, /* ffactor */
62 256, /* nelem */
63 2048 * 1024, /* cachesize */
64 NULL, /* hash() */
65 BIG_ENDIAN /* lorder */
66 };
67
68 static enum state { FILE_INSECURE, FILE_SECURE, FILE_ORIG } clean;
69 static struct passwd pwd; /* password structure */
70 static char *pname; /* password file name */
71 static char prefix[MAXPATHLEN];
72
73 static int is_comment; /* flag for comments */
74 static char line[LINE_MAX];
75
76 void cleanup(void);
77 void error(const char *);
78 void cp(char *, char *, mode_t mode);
79 void mv(char *, char *);
80 int scan(FILE *, struct passwd *);
81 static void usage(void);
82
83 int
main(int argc,char * argv[])84 main(int argc, char *argv[])
85 {
86 static char verskey[] = _PWD_VERSION_KEY;
87 char version = _PWD_CURRENT_VERSION;
88 DB *dp, *sdp, *pw_db;
89 DBT data, sdata, key;
90 FILE *fp, *oldfp;
91 sigset_t set;
92 int ch, cnt, ypcnt, makeold, tfd, yp_enabled = 0;
93 unsigned int len;
94 uint32_t store;
95 const char *t;
96 char *p;
97 char buf[MAX(MAXPATHLEN, LINE_MAX * 2)];
98 char sbuf[MAX(MAXPATHLEN, LINE_MAX * 2)];
99 char buf2[MAXPATHLEN];
100 char sbuf2[MAXPATHLEN];
101 char tbuf[1024];
102 char *username;
103 u_int method, methoduid;
104 int Cflag, dflag, iflag;
105 int nblock = 0;
106
107 iflag = dflag = Cflag = 0;
108 strcpy(prefix, _PATH_PWD);
109 makeold = 0;
110 username = NULL;
111 oldfp = NULL;
112 while ((ch = getopt(argc, argv, "Cd:iNps:u:v")) != -1)
113 switch(ch) {
114 case 'C': /* verify only */
115 Cflag = 1;
116 break;
117 case 'd':
118 dflag++;
119 strlcpy(prefix, optarg, sizeof(prefix));
120 break;
121 case 'i':
122 iflag++;
123 break;
124 case 'N': /* do not wait for lock */
125 nblock = LOCK_NB; /* will fail if locked */
126 break;
127 case 'p': /* create V7 "file.orig" */
128 makeold = 1;
129 break;
130 case 's': /* change default cachesize */
131 openinfo.cachesize = atoi(optarg) * 1024 * 1024;
132 break;
133 case 'u': /* only update this record */
134 username = optarg;
135 break;
136 case 'v': /* backward compatible */
137 break;
138 default:
139 usage();
140 }
141 argc -= optind;
142 argv += optind;
143
144 if (argc != 1 || (username && (*username == '+' || *username == '-')))
145 usage();
146
147 /*
148 * This could be changed to allow the user to interrupt.
149 * Probably not worth the effort.
150 */
151 sigemptyset(&set);
152 sigaddset(&set, SIGTSTP);
153 sigaddset(&set, SIGHUP);
154 sigaddset(&set, SIGINT);
155 sigaddset(&set, SIGQUIT);
156 sigaddset(&set, SIGTERM);
157 (void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL);
158
159 /* We don't care what the user wants. */
160 (void)umask(0);
161
162 pname = *argv;
163
164 /*
165 * Open and lock the original password file. We have to check
166 * the hardlink count after we get the lock to handle any potential
167 * unlink/rename race.
168 *
169 * This lock is necessary when someone runs pwd_mkdb manually, directly
170 * on master.passwd, to handle the case where a user might try to
171 * change his password while pwd_mkdb is running.
172 */
173 for (;;) {
174 struct stat st;
175
176 if (!(fp = fopen(pname, "r")))
177 error(pname);
178 if (flock(fileno(fp), LOCK_EX|nblock) < 0 && !(dflag && iflag))
179 error("flock");
180 if (fstat(fileno(fp), &st) < 0)
181 error(pname);
182 if (st.st_nlink != 0)
183 break;
184 fclose(fp);
185 fp = NULL;
186 }
187
188 /* check only if password database is valid */
189 if (Cflag) {
190 while (scan(fp, &pwd))
191 if (!is_comment && strlen(pwd.pw_name) >= MAXLOGNAME) {
192 warnx("%s: username too long", pwd.pw_name);
193 exit(1);
194 }
195 exit(0);
196 }
197
198 /* Open the temporary insecure password database. */
199 (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
200 (void)snprintf(sbuf, sizeof(sbuf), "%s/%s.tmp", prefix, _SMP_DB);
201 if (username) {
202 int use_version;
203
204 (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB);
205 (void)snprintf(sbuf2, sizeof(sbuf2), "%s/%s", prefix, _SMP_DB);
206
207 clean = FILE_INSECURE;
208 cp(buf2, buf, PERM_INSECURE);
209 dp = dbopen(buf,
210 O_RDWR|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
211 if (dp == NULL)
212 error(buf);
213
214 clean = FILE_SECURE;
215 cp(sbuf2, sbuf, PERM_SECURE);
216 sdp = dbopen(sbuf,
217 O_RDWR|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
218 if (sdp == NULL)
219 error(sbuf);
220
221 /*
222 * Do some trouble to check if we should store this users
223 * uid. Don't use getpwnam/getpwuid as that interferes
224 * with NIS.
225 */
226 pw_db = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL);
227 if (!pw_db)
228 error(_MP_DB);
229
230 key.data = verskey;
231 key.size = sizeof(verskey)-1;
232 if ((pw_db->get)(pw_db, &key, &data, 0) == 0)
233 use_version = *(unsigned char *)data.data;
234 else
235 use_version = 3;
236 buf[0] = _PW_VERSIONED(_PW_KEYBYNAME, use_version);
237 len = strlen(username);
238
239 /* Only check that username fits in buffer */
240 memmove(buf + 1, username, MIN(len, sizeof(buf) - 1));
241 key.data = (u_char *)buf;
242 key.size = len + 1;
243 if ((pw_db->get)(pw_db, &key, &data, 0) == 0) {
244 p = (char *)data.data;
245
246 /* jump over pw_name and pw_passwd, to get to pw_uid */
247 while (*p++)
248 ;
249 while (*p++)
250 ;
251
252 buf[0] = _PW_VERSIONED(_PW_KEYBYUID, use_version);
253 memmove(buf + 1, p, sizeof(store));
254 key.data = (u_char *)buf;
255 key.size = sizeof(store) + 1;
256
257 if ((pw_db->get)(pw_db, &key, &data, 0) == 0) {
258 /* First field of data.data holds pw_pwname */
259 if (!strcmp(data.data, username))
260 methoduid = 0;
261 else
262 methoduid = R_NOOVERWRITE;
263 } else {
264 methoduid = R_NOOVERWRITE;
265 }
266 } else {
267 methoduid = R_NOOVERWRITE;
268 }
269 if ((pw_db->close)(pw_db))
270 error("close pw_db");
271 method = 0;
272 } else {
273 dp = dbopen(buf,
274 O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
275 if (dp == NULL)
276 error(buf);
277 clean = FILE_INSECURE;
278
279 sdp = dbopen(sbuf,
280 O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
281 if (sdp == NULL)
282 error(sbuf);
283 clean = FILE_SECURE;
284
285 method = R_NOOVERWRITE;
286 methoduid = R_NOOVERWRITE;
287 }
288
289 /*
290 * Open file for old password file. Minor trickiness -- don't want to
291 * chance the file already existing, since someone (stupidly) might
292 * still be using this for permission checking. So, open it first and
293 * fdopen the resulting fd. The resulting file should be readable by
294 * everyone.
295 */
296 if (makeold) {
297 (void)snprintf(buf, sizeof(buf), "%s.orig", pname);
298 if ((tfd = open(buf,
299 O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0)
300 error(buf);
301 if ((oldfp = fdopen(tfd, "w")) == NULL)
302 error(buf);
303 clean = FILE_ORIG;
304 }
305
306 /*
307 * The databases actually contain three copies of the original data.
308 * Each password file entry is converted into a rough approximation
309 * of a ``struct passwd'', with the strings placed inline. This
310 * object is then stored as the data for three separate keys. The
311 * first key * is the pw_name field prepended by the _PW_KEYBYNAME
312 * character. The second key is the pw_uid field prepended by the
313 * _PW_KEYBYUID character. The third key is the line number in the
314 * original file prepended by the _PW_KEYBYNUM character. (The special
315 * characters are prepended to ensure that the keys do not collide.)
316 */
317 /* In order to transition this file into a machine-independent
318 * form, we have to change the format of entries. However, since
319 * older binaries will still expect the old MD format entries, we
320 * create those as usual and use versioned tags for the new entries.
321 */
322 if (username == NULL) {
323 /* Do not add the VERSION tag when updating a single
324 * user. When operating on `old format' databases, this
325 * would result in applications `seeing' only the updated
326 * entries.
327 */
328 key.data = verskey;
329 key.size = sizeof(verskey)-1;
330 data.data = &version;
331 data.size = 1;
332 if ((dp->put)(dp, &key, &data, 0) == -1)
333 error("put");
334 if ((sdp->put)(sdp, &key, &data, 0) == -1)
335 error("put");
336 }
337 ypcnt = 0;
338 data.data = (u_char *)buf;
339 sdata.data = (u_char *)sbuf;
340 key.data = (u_char *)tbuf;
341 for (cnt = 1; scan(fp, &pwd); ++cnt) {
342 if (!is_comment &&
343 (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-')) {
344 yp_enabled = 1;
345 ypcnt++;
346 }
347 if (is_comment)
348 --cnt;
349 #define COMPACT(e) t = e; while ((*p++ = *t++));
350 #define SCALAR(e) store = htonl((uint32_t)(e)); \
351 memmove(p, &store, sizeof(store)); \
352 p += sizeof(store);
353 #define LSCALAR(e) store = HTOL((uint32_t)(e)); \
354 memmove(p, &store, sizeof(store)); \
355 p += sizeof(store);
356 #define HTOL(e) (openinfo.lorder == BYTE_ORDER ? \
357 (uint32_t)(e) : \
358 bswap32((uint32_t)(e)))
359 if (!is_comment &&
360 (!username || (strcmp(username, pwd.pw_name) == 0))) {
361 /* Create insecure data. */
362 p = buf;
363 COMPACT(pwd.pw_name);
364 COMPACT("*");
365 SCALAR(pwd.pw_uid);
366 SCALAR(pwd.pw_gid);
367 SCALAR(pwd.pw_change);
368 COMPACT(pwd.pw_class);
369 COMPACT(pwd.pw_gecos);
370 COMPACT(pwd.pw_dir);
371 COMPACT(pwd.pw_shell);
372 SCALAR(pwd.pw_expire);
373 SCALAR(pwd.pw_fields);
374 data.size = p - buf;
375
376 /* Create secure data. */
377 p = sbuf;
378 COMPACT(pwd.pw_name);
379 COMPACT(pwd.pw_passwd);
380 SCALAR(pwd.pw_uid);
381 SCALAR(pwd.pw_gid);
382 SCALAR(pwd.pw_change);
383 COMPACT(pwd.pw_class);
384 COMPACT(pwd.pw_gecos);
385 COMPACT(pwd.pw_dir);
386 COMPACT(pwd.pw_shell);
387 SCALAR(pwd.pw_expire);
388 SCALAR(pwd.pw_fields);
389 sdata.size = p - sbuf;
390
391 /* Store insecure by name. */
392 tbuf[0] = CURRENT_VERSION(_PW_KEYBYNAME);
393 len = strlen(pwd.pw_name);
394 memmove(tbuf + 1, pwd.pw_name, len);
395 key.size = len + 1;
396 if ((dp->put)(dp, &key, &data, method) == -1)
397 error("put");
398
399 /* Store insecure by number. */
400 tbuf[0] = CURRENT_VERSION(_PW_KEYBYNUM);
401 store = htonl(cnt);
402 memmove(tbuf + 1, &store, sizeof(store));
403 key.size = sizeof(store) + 1;
404 if ((dp->put)(dp, &key, &data, method) == -1)
405 error("put");
406
407 /* Store insecure by uid. */
408 tbuf[0] = CURRENT_VERSION(_PW_KEYBYUID);
409 store = htonl(pwd.pw_uid);
410 memmove(tbuf + 1, &store, sizeof(store));
411 key.size = sizeof(store) + 1;
412 if ((dp->put)(dp, &key, &data, methoduid) == -1)
413 error("put");
414
415 /* Store secure by name. */
416 tbuf[0] = CURRENT_VERSION(_PW_KEYBYNAME);
417 len = strlen(pwd.pw_name);
418 memmove(tbuf + 1, pwd.pw_name, len);
419 key.size = len + 1;
420 if ((sdp->put)(sdp, &key, &sdata, method) == -1)
421 error("put");
422
423 /* Store secure by number. */
424 tbuf[0] = CURRENT_VERSION(_PW_KEYBYNUM);
425 store = htonl(cnt);
426 memmove(tbuf + 1, &store, sizeof(store));
427 key.size = sizeof(store) + 1;
428 if ((sdp->put)(sdp, &key, &sdata, method) == -1)
429 error("put");
430
431 /* Store secure by uid. */
432 tbuf[0] = CURRENT_VERSION(_PW_KEYBYUID);
433 store = htonl(pwd.pw_uid);
434 memmove(tbuf + 1, &store, sizeof(store));
435 key.size = sizeof(store) + 1;
436 if ((sdp->put)(sdp, &key, &sdata, methoduid) == -1)
437 error("put");
438
439 /* Store insecure and secure special plus and special minus */
440 if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') {
441 tbuf[0] = CURRENT_VERSION(_PW_KEYYPBYNUM);
442 store = htonl(ypcnt);
443 memmove(tbuf + 1, &store, sizeof(store));
444 key.size = sizeof(store) + 1;
445 if ((dp->put)(dp, &key, &data, method) == -1)
446 error("put");
447 if ((sdp->put)(sdp, &key, &sdata, method) == -1)
448 error("put");
449 }
450 }
451 /*
452 * Create original style password file entry.
453 *
454 * Don't copy comments since this could reveal encrypted
455 * passwords if entries have been simply commented out
456 * in master.passwd.
457 */
458 if (makeold && !is_comment) {
459 char uidstr[20];
460 char gidstr[20];
461
462 snprintf(uidstr, sizeof(uidstr), "%u", pwd.pw_uid);
463 snprintf(gidstr, sizeof(gidstr), "%u", pwd.pw_gid);
464
465 if (fprintf(oldfp, "%s:*:%s:%s:%s:%s:%s\n",
466 pwd.pw_name, pwd.pw_fields & _PWF_UID ? uidstr : "",
467 pwd.pw_fields & _PWF_GID ? gidstr : "",
468 pwd.pw_gecos, pwd.pw_dir, pwd.pw_shell) < 0)
469 error("write old");
470 }
471 }
472 /* If YP enabled, set flag. */
473 if (yp_enabled) {
474 buf[0] = yp_enabled + 2;
475 data.size = 1;
476 key.size = 1;
477 tbuf[0] = CURRENT_VERSION(_PW_KEYYPENABLED);
478 if ((dp->put)(dp, &key, &data, method) == -1)
479 error("put");
480 if ((sdp->put)(sdp, &key, &data, method) == -1)
481 error("put");
482 }
483
484 if ((dp->close)(dp) == -1)
485 error("close");
486 if ((sdp->close)(sdp) == -1)
487 error("close");
488 if (makeold) {
489 (void)fflush(oldfp);
490 if (fclose(oldfp) == EOF)
491 error("close old");
492 }
493
494 /* Set master.passwd permissions, in case caller forgot. */
495 (void)fchmod(fileno(fp), S_IRUSR|S_IWUSR);
496
497 /* Install as the real password files. */
498 (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
499 (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB);
500 mv(buf, buf2);
501 (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB);
502 (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _SMP_DB);
503 mv(buf, buf2);
504 if (makeold) {
505 (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _PASSWD);
506 (void)snprintf(buf, sizeof(buf), "%s.orig", pname);
507 mv(buf, buf2);
508 }
509 /*
510 * Move the master password LAST -- chpass(1), passwd(1) and vipw(8)
511 * all use flock(2) on it to block other incarnations of themselves.
512 * The rename means that everything is unlocked, as the original file
513 * can no longer be accessed.
514 */
515 (void)snprintf(buf, sizeof(buf), "%s/%s", prefix, _MASTERPASSWD);
516 mv(pname, buf);
517
518 /*
519 * Close locked password file after rename()
520 */
521 if (fclose(fp) == EOF)
522 error("close fp");
523
524 exit(0);
525 }
526
527 int
scan(FILE * fp,struct passwd * pw)528 scan(FILE *fp, struct passwd *pw)
529 {
530 static int lcnt;
531 size_t len;
532 char *p;
533
534 p = fgetln(fp, &len);
535 if (p == NULL)
536 return (0);
537 ++lcnt;
538 /*
539 * ``... if I swallow anything evil, put your fingers down my
540 * throat...''
541 * -- The Who
542 */
543 if (len > 0 && p[len - 1] == '\n')
544 len--;
545 if (len >= sizeof(line) - 1) {
546 warnx("line #%d too long", lcnt);
547 goto fmt;
548 }
549 memcpy(line, p, len);
550 line[len] = '\0';
551
552 /*
553 * Ignore comments: ^[ \t]*#
554 */
555 for (p = line; *p != '\0'; p++)
556 if (*p != ' ' && *p != '\t')
557 break;
558 if (*p == '#' || *p == '\0') {
559 is_comment = 1;
560 return(1);
561 } else
562 is_comment = 0;
563
564 if (!__pw_scan(line, pw, _PWSCAN_WARN|_PWSCAN_MASTER)) {
565 warnx("at line #%d", lcnt);
566 fmt: errno = EFTYPE; /* XXX */
567 error(pname);
568 }
569
570 return (1);
571 }
572
573 void
cp(char * from,char * to,mode_t mode)574 cp(char *from, char *to, mode_t mode)
575 {
576 static char buf[MAXBSIZE];
577 int from_fd, rcount, to_fd, wcount;
578
579 if ((from_fd = open(from, O_RDONLY, 0)) < 0)
580 error(from);
581 if ((to_fd = open(to, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0)
582 error(to);
583 while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
584 wcount = write(to_fd, buf, rcount);
585 if (rcount != wcount || wcount == -1) {
586 int sverrno = errno;
587
588 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
589 errno = sverrno;
590 error(buf);
591 }
592 }
593 if (rcount < 0) {
594 int sverrno = errno;
595
596 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
597 errno = sverrno;
598 error(buf);
599 }
600 }
601
602
603 void
mv(char * from,char * to)604 mv(char *from, char *to)
605 {
606 char buf[MAXPATHLEN];
607 char *to_dir;
608 int to_dir_fd = -1;
609
610 /*
611 * Make sure file is safe on disk. To improve performance we will call
612 * fsync() to the directory where file lies
613 */
614 if (rename(from, to) != 0 ||
615 (to_dir = dirname(to)) == NULL ||
616 (to_dir_fd = open(to_dir, O_RDONLY|O_DIRECTORY)) == -1 ||
617 fsync(to_dir_fd) != 0) {
618 int sverrno = errno;
619 (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
620 errno = sverrno;
621 if (to_dir_fd != -1)
622 close(to_dir_fd);
623 error(buf);
624 }
625
626 if (to_dir_fd != -1)
627 close(to_dir_fd);
628 }
629
630 void
error(const char * name)631 error(const char *name)
632 {
633
634 warn("%s", name);
635 cleanup();
636 exit(1);
637 }
638
639 void
cleanup(void)640 cleanup(void)
641 {
642 char buf[MAXPATHLEN];
643
644 switch(clean) {
645 case FILE_ORIG:
646 (void)snprintf(buf, sizeof(buf), "%s.orig", pname);
647 (void)unlink(buf);
648 /* FALLTHROUGH */
649 case FILE_SECURE:
650 (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB);
651 (void)unlink(buf);
652 /* FALLTHROUGH */
653 case FILE_INSECURE:
654 (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
655 (void)unlink(buf);
656 }
657 }
658
659 static void
usage(void)660 usage(void)
661 {
662
663 (void)fprintf(stderr,
664 "usage: pwd_mkdb [-CiNp] [-d directory] [-s cachesize] [-u username] file\n");
665 exit(1);
666 }
667