Fix a bug when '-f -H' is used and the target already exists. cpdup was
[dragonfly.git] / contrib / sendmail-8.13.8 / makemap / makemap.c
1 /*
2  * Copyright (c) 1998-2002, 2004 Sendmail, Inc. and its suppliers.
3  *      All rights reserved.
4  * Copyright (c) 1992 Eric P. Allman.  All rights reserved.
5  * Copyright (c) 1992, 1993
6  *      The Regents of the University of California.  All rights reserved.
7  *
8  * By using this file, you agree to the terms and conditions set
9  * forth in the LICENSE file which can be found at the top level of
10  * the sendmail distribution.
11  *
12  */
13
14 #include <sm/gen.h>
15
16 SM_IDSTR(copyright,
17 "@(#) Copyright (c) 1998-2002, 2004 Sendmail, Inc. and its suppliers.\n\
18         All rights reserved.\n\
19      Copyright (c) 1992 Eric P. Allman.  All rights reserved.\n\
20      Copyright (c) 1992, 1993\n\
21         The Regents of the University of California.  All rights reserved.\n")
22
23 SM_IDSTR(id, "@(#)$Id: makemap.c,v 8.177 2004/08/03 23:57:24 ca Exp $")
24
25
26 #include <sys/types.h>
27 #ifndef ISC_UNIX
28 # include <sys/file.h>
29 #endif /* ! ISC_UNIX */
30 #include <ctype.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #ifdef EX_OK
34 # undef EX_OK           /* unistd.h may have another use for this */
35 #endif /* EX_OK */
36 #include <sysexits.h>
37 #include <sendmail/sendmail.h>
38 #include <sendmail/pathnames.h>
39 #include <libsmdb/smdb.h>
40
41 uid_t   RealUid;
42 gid_t   RealGid;
43 char    *RealUserName;
44 uid_t   RunAsUid;
45 uid_t   RunAsGid;
46 char    *RunAsUserName;
47 int     Verbose = 2;
48 bool    DontInitGroups = false;
49 uid_t   TrustedUid = 0;
50 BITMAP256 DontBlameSendmail;
51
52 #define BUFSIZE         1024
53 #define ISSEP(c) (sep == '\0' ? isascii(c) && isspace(c) : (c) == sep)
54
55 static void usage __P((char *));
56
57 static void
58 usage(progname)
59         char *progname;
60 {
61         sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
62                       "Usage: %s [-C cffile] [-N] [-c cachesize] [-D commentchar]\n",
63                       progname);
64         sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
65                       "       %*s [-d] [-e] [-f] [-l] [-o] [-r] [-s] [-t delimiter]\n",
66                       (int) strlen(progname), "");
67         sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
68                       "       %*s [-u] [-v] type mapname\n",
69                       (int) strlen(progname), "");
70         exit(EX_USAGE);
71 }
72
73 int
74 main(argc, argv)
75         int argc;
76         char **argv;
77 {
78         char *progname;
79         char *cfile;
80         bool inclnull = false;
81         bool notrunc = false;
82         bool allowreplace = false;
83         bool allowempty = false;
84         bool verbose = false;
85         bool foldcase = true;
86         bool unmake = false;
87         char sep = '\0';
88         char comment = '#';
89         int exitstat;
90         int opt;
91         char *typename = NULL;
92         char *mapname = NULL;
93         unsigned int lineno;
94         int st;
95         int mode;
96         int smode;
97         int putflags = 0;
98         long sff = SFF_ROOTOK|SFF_REGONLY;
99         struct passwd *pw;
100         SMDB_DATABASE *database;
101         SMDB_CURSOR *cursor;
102         SMDB_DBENT db_key, db_val;
103         SMDB_DBPARAMS params;
104         SMDB_USER_INFO user_info;
105         char ibuf[BUFSIZE];
106 #if HASFCHOWN
107         SM_FILE_T *cfp;
108         char buf[MAXLINE];
109 #endif /* HASFCHOWN */
110         static char rnamebuf[MAXNAME];  /* holds RealUserName */
111         extern char *optarg;
112         extern int optind;
113
114         memset(&params, '\0', sizeof params);
115         params.smdbp_cache_size = 1024 * 1024;
116
117         progname = strrchr(argv[0], '/');
118         if (progname != NULL)
119                 progname++;
120         else
121                 progname = argv[0];
122         cfile = getcfname(0, 0, SM_GET_SENDMAIL_CF, NULL);
123
124         clrbitmap(DontBlameSendmail);
125         RunAsUid = RealUid = getuid();
126         RunAsGid = RealGid = getgid();
127         pw = getpwuid(RealUid);
128         if (pw != NULL)
129                 (void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf);
130         else
131                 (void) sm_snprintf(rnamebuf, sizeof rnamebuf,
132                     "Unknown UID %d", (int) RealUid);
133         RunAsUserName = RealUserName = rnamebuf;
134         user_info.smdbu_id = RunAsUid;
135         user_info.smdbu_group_id = RunAsGid;
136         (void) sm_strlcpy(user_info.smdbu_name, RunAsUserName,
137                        SMDB_MAX_USER_NAME_LEN);
138
139 #define OPTIONS         "C:D:Nc:deflorst:uv"
140         while ((opt = getopt(argc, argv, OPTIONS)) != -1)
141         {
142                 switch (opt)
143                 {
144                   case 'C':
145                         cfile = optarg;
146                         break;
147
148                   case 'N':
149                         inclnull = true;
150                         break;
151
152                   case 'c':
153                         params.smdbp_cache_size = atol(optarg);
154                         break;
155
156                   case 'd':
157                         params.smdbp_allow_dup = true;
158                         break;
159
160                   case 'e':
161                         allowempty = true;
162                         break;
163
164                   case 'f':
165                         foldcase = false;
166                         break;
167
168                   case 'D':
169                         comment = *optarg;
170                         break;
171
172                   case 'l':
173                         smdb_print_available_types();
174                         exit(EX_OK);
175                         break;
176
177                   case 'o':
178                         notrunc = true;
179                         break;
180
181                   case 'r':
182                         allowreplace = true;
183                         break;
184
185                   case 's':
186                         setbitn(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail);
187                         setbitn(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail);
188                         setbitn(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail);
189                         setbitn(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail);
190                         break;
191
192                   case 't':
193                         if (optarg == NULL || *optarg == '\0')
194                         {
195                                 sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
196                                               "Invalid separator\n");
197                                 break;
198                         }
199                         sep = *optarg;
200                         break;
201
202                   case 'u':
203                         unmake = true;
204                         break;
205
206                   case 'v':
207                         verbose = true;
208                         break;
209
210                   default:
211                         usage(progname);
212                         /* NOTREACHED */
213                 }
214         }
215
216         if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
217                 sff |= SFF_NOSLINK;
218         if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
219                 sff |= SFF_NOHLINK;
220         if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
221                 sff |= SFF_NOWLINK;
222
223         argc -= optind;
224         argv += optind;
225         if (argc != 2)
226         {
227                 usage(progname);
228                 /* NOTREACHED */
229         }
230         else
231         {
232                 typename = argv[0];
233                 mapname = argv[1];
234         }
235
236 #if HASFCHOWN
237         /* Find TrustedUser value in sendmail.cf */
238         if ((cfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, cfile, SM_IO_RDONLY,
239                               NULL)) == NULL)
240         {
241                 sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "makemap: %s: %s",
242                               cfile, sm_errstring(errno));
243                 exit(EX_NOINPUT);
244         }
245         while (sm_io_fgets(cfp, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL)
246         {
247                 register char *b;
248
249                 if ((b = strchr(buf, '\n')) != NULL)
250                         *b = '\0';
251
252                 b = buf;
253                 switch (*b++)
254                 {
255                   case 'O':             /* option */
256                         if (strncasecmp(b, " TrustedUser", 12) == 0 &&
257                             !(isascii(b[12]) && isalnum(b[12])))
258                         {
259                                 b = strchr(b, '=');
260                                 if (b == NULL)
261                                         continue;
262                                 while (isascii(*++b) && isspace(*b))
263                                         continue;
264                                 if (isascii(*b) && isdigit(*b))
265                                         TrustedUid = atoi(b);
266                                 else
267                                 {
268                                         TrustedUid = 0;
269                                         pw = getpwnam(b);
270                                         if (pw == NULL)
271                                                 (void) sm_io_fprintf(smioerr,
272                                                                      SM_TIME_DEFAULT,
273                                                                      "TrustedUser: unknown user %s\n", b);
274                                         else
275                                                 TrustedUid = pw->pw_uid;
276                                 }
277
278 # ifdef UID_MAX
279                                 if (TrustedUid > UID_MAX)
280                                 {
281                                         (void) sm_io_fprintf(smioerr,
282                                                              SM_TIME_DEFAULT,
283                                                              "TrustedUser: uid value (%ld) > UID_MAX (%ld)",
284                                                 (long) TrustedUid,
285                                                 (long) UID_MAX);
286                                         TrustedUid = 0;
287                                 }
288 # endif /* UID_MAX */
289                                 break;
290                         }
291
292
293                   default:
294                         continue;
295                 }
296         }
297         (void) sm_io_close(cfp, SM_TIME_DEFAULT);
298 #endif /* HASFCHOWN */
299
300         if (!params.smdbp_allow_dup && !allowreplace)
301                 putflags = SMDBF_NO_OVERWRITE;
302
303         if (unmake)
304         {
305                 mode = O_RDONLY;
306                 smode = S_IRUSR;
307         }
308         else
309         {
310                 mode = O_RDWR;
311                 if (!notrunc)
312                 {
313                         mode |= O_CREAT|O_TRUNC;
314                         sff |= SFF_CREAT;
315                 }
316                 smode = S_IWUSR;
317         }
318
319         params.smdbp_num_elements = 4096;
320
321         errno = smdb_open_database(&database, mapname, mode, smode, sff,
322                                    typename, &user_info, &params);
323         if (errno != SMDBE_OK)
324         {
325                 char *hint;
326
327                 if (errno == SMDBE_UNSUPPORTED_DB_TYPE &&
328                     (hint = smdb_db_definition(typename)) != NULL)
329                         (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
330                                              "%s: Need to recompile with -D%s for %s support\n",
331                                              progname, hint, typename);
332                 else
333                         (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
334                                              "%s: error opening type %s map %s: %s\n",
335                                              progname, typename, mapname,
336                                              sm_errstring(errno));
337                 exit(EX_CANTCREAT);
338         }
339
340         (void) database->smdb_sync(database, 0);
341
342         if (!unmake && geteuid() == 0 && TrustedUid != 0)
343         {
344                 errno = database->smdb_set_owner(database, TrustedUid, -1);
345                 if (errno != SMDBE_OK)
346                 {
347                         (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
348                                              "WARNING: ownership change on %s failed %s",
349                                              mapname, sm_errstring(errno));
350                 }
351         }
352
353         /*
354         **  Copy the data
355         */
356
357         exitstat = EX_OK;
358         if (unmake)
359         {
360                 errno = database->smdb_cursor(database, &cursor, 0);
361                 if (errno != SMDBE_OK)
362                 {
363
364                         (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
365                                              "%s: cannot make cursor for type %s map %s\n",
366                                              progname, typename, mapname);
367                         exit(EX_SOFTWARE);
368                 }
369
370                 memset(&db_key, '\0', sizeof db_key);
371                 memset(&db_val, '\0', sizeof db_val);
372
373                 for (lineno = 0; ; lineno++)
374                 {
375                         errno = cursor->smdbc_get(cursor, &db_key, &db_val,
376                                                   SMDB_CURSOR_GET_NEXT);
377                         if (errno != SMDBE_OK)
378                                 break;
379
380                         (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
381                                              "%.*s\t%.*s\n",
382                                              (int) db_key.size,
383                                              (char *) db_key.data,
384                                              (int) db_val.size,
385                                              (char *)db_val.data);
386
387                 }
388                 (void) cursor->smdbc_close(cursor);
389         }
390         else
391         {
392                 lineno = 0;
393                 while (sm_io_fgets(smioin, SM_TIME_DEFAULT, ibuf, sizeof ibuf)
394                        != NULL)
395                 {
396                         register char *p;
397
398                         lineno++;
399
400                         /*
401                         **  Parse the line.
402                         */
403
404                         p = strchr(ibuf, '\n');
405                         if (p != NULL)
406                                 *p = '\0';
407                         else if (!sm_io_eof(smioin))
408                         {
409                                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
410                                                      "%s: %s: line %u: line too long (%ld bytes max)\n",
411                                                      progname, mapname, lineno,
412                                                      (long) sizeof ibuf);
413                                 exitstat = EX_DATAERR;
414                                 continue;
415                         }
416
417                         if (ibuf[0] == '\0' || ibuf[0] == comment)
418                                 continue;
419                         if (sep == '\0' && isascii(ibuf[0]) && isspace(ibuf[0]))
420                         {
421                                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
422                                                      "%s: %s: line %u: syntax error (leading space)\n",
423                                                      progname, mapname, lineno);
424                                 exitstat = EX_DATAERR;
425                                 continue;
426                         }
427
428                         memset(&db_key, '\0', sizeof db_key);
429                         memset(&db_val, '\0', sizeof db_val);
430                         db_key.data = ibuf;
431
432                         for (p = ibuf; *p != '\0' && !(ISSEP(*p)); p++)
433                         {
434                                 if (foldcase && isascii(*p) && isupper(*p))
435                                         *p = tolower(*p);
436                         }
437                         db_key.size = p - ibuf;
438                         if (inclnull)
439                                 db_key.size++;
440
441                         if (*p != '\0')
442                                 *p++ = '\0';
443                         while (*p != '\0' && ISSEP(*p))
444                                 p++;
445                         if (!allowempty && *p == '\0')
446                         {
447                                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
448                                                      "%s: %s: line %u: no RHS for LHS %s\n",
449                                                      progname, mapname, lineno,
450                                                      (char *) db_key.data);
451                                 exitstat = EX_DATAERR;
452                                 continue;
453                         }
454
455                         db_val.data = p;
456                         db_val.size = strlen(p);
457                         if (inclnull)
458                                 db_val.size++;
459
460                         /*
461                         **  Do the database insert.
462                         */
463
464                         if (verbose)
465                         {
466                                 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
467                                                      "key=`%s', val=`%s'\n",
468                                                      (char *) db_key.data,
469                                                      (char *) db_val.data);
470                         }
471
472                         errno = database->smdb_put(database, &db_key, &db_val,
473                                                    putflags);
474                         switch (errno)
475                         {
476                           case SMDBE_KEY_EXIST:
477                                 st = 1;
478                                 break;
479
480                           case 0:
481                                 st = 0;
482                                 break;
483
484                           default:
485                                 st = -1;
486                                 break;
487                         }
488
489                         if (st < 0)
490                         {
491                                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
492                                                      "%s: %s: line %u: key %s: put error: %s\n",
493                                                      progname, mapname, lineno,
494                                                      (char *) db_key.data,
495                                                      sm_errstring(errno));
496                                 exitstat = EX_IOERR;
497                         }
498                         else if (st > 0)
499                         {
500                                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
501                                                      "%s: %s: line %u: key %s: duplicate key\n",
502                                                      progname, mapname,
503                                                      lineno,
504                                                      (char *) db_key.data);
505                                 exitstat = EX_DATAERR;
506                         }
507                 }
508         }
509
510         /*
511         **  Now close the database.
512         */
513
514         errno = database->smdb_close(database);
515         if (errno != SMDBE_OK)
516         {
517                 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
518                                      "%s: close(%s): %s\n",
519                                      progname, mapname, sm_errstring(errno));
520                 exitstat = EX_IOERR;
521         }
522         smdb_free_database(database);
523
524         exit(exitstat);
525
526         /* NOTREACHED */
527         return exitstat;
528 }