"Fossies" - the Fresh Open Source Software Archive

Member "amavisd-milter-1.7.2/amavisd-milter/main.c" (27 Jan 2019, 16694 Bytes) of package /linux/privat/amavisd-milter-1.7.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "main.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * Copyright (c) 2005, Petr Rehor <rx@rx.cz>. All rights reserved.
    3  *
    4  * Redistribution and use in source and binary forms, with or without
    5  * modification, are permitted provided that the following conditions
    6  * are met:
    7  * 1. Redistributions of source code must retain the above copyright
    8  *    notice, this list of conditions and the following disclaimer.
    9  * 2. Redistributions in binary form must reproduce the above copyright
   10  *    notice, this list of conditions and the following disclaimer in the
   11  *    documentation and/or other materials provided with the distribution.
   12  * 3. Neither the name of the copyright holders nor the names of its
   13  *    contributors may be used to endorse or promote products derived from
   14  *    this software without specific prior written permission.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   19  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   20  * OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
   22  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
   23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
   24  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   25  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   27  */
   28 
   29 #include "amavisd-milter.h"
   30 
   31 #include <stdarg.h>
   32 #include <sys/socket.h>
   33 #include <sysexits.h>
   34 
   35 
   36 /*
   37 ** GLOBAL VARIABLES
   38 */
   39 int             daemonize = 1;
   40 int             daemonized = 0;
   41 int             debug_level = LOG_WARNING;
   42 int             max_conns = 0;
   43 int             max_wait = 5 * 60;
   44 sem_t           max_sem_t;
   45 sem_t          *max_sem = NULL;
   46 const char     *pid_file = LOCAL_STATE_DIR "/" PACKAGE ".pid";
   47 char           *mlfi_socket = LOCAL_STATE_DIR "/" PACKAGE ".sock";
   48 #ifdef HAVE_SMFI_SETBACKLOG
   49 int             mlfi_socket_backlog = 0;
   50 #endif
   51 long            mlfi_timeout = 600;
   52 const char     *amavisd_socket = LOCAL_STATE_DIR "/amavisd.sock";
   53 long            amavisd_timeout = 600;
   54 int             ignore_amavisd_error = 0;
   55 const char     *working_dir = WORKING_DIR;
   56 const char     *delivery_care_of = "client";
   57 int             policybank_from_daemon_name = 0;
   58 
   59 
   60 /*
   61 ** USAGE - Print usage info
   62 */
   63 static void
   64 usage(const char *progname)
   65 {
   66     (void) fprintf(stdout, "\nUsage: %s [OPTIONS]\n", progname);
   67     (void) fprintf(stdout, "Options are:\n");
   68     (void) fprintf(stdout, "    -B                      Use daemon_name policy bank\n");
   69     (void) fprintf(stdout, "    -d debug-level          Set debug level\n");
   70     (void) fprintf(stdout, "    -D delivery             Delivery care of server or client\n");
   71     (void) fprintf(stdout, "    -f                      Run in the foreground\n");
   72     (void) fprintf(stdout, "    -h                      Print this page\n");
   73     (void) fprintf(stdout, "    -m max-conns            Maximum amavisd connections \n");
   74     (void) fprintf(stdout, "    -M max-wait             Maximum wait for connection in seconds\n");
   75     (void) fprintf(stdout, "    -p pidfile              Use this pid file\n");
   76     (void) fprintf(stdout, "    -P                      When amavisd fails mail will be passed\n                                through unchecked\n");
   77 #ifdef HAVE_SMFI_SETBACKLOG
   78     (void) fprintf(stdout, "    -q backlog              Milter communication socket backlog\n");
   79 #endif
   80     (void) fprintf(stdout, "    -s socket               Milter communication socket\n");
   81     (void) fprintf(stdout, "    -S socket               Amavisd communication socket\n");
   82     (void) fprintf(stdout, "    -t timeout              Milter connection timeout in seconds\n");
   83     (void) fprintf(stdout, "    -T timeout              Amavisd connection timeout in seconds\n");
   84     (void) fprintf(stdout, "    -v                      Report the version and exit\n");
   85     (void) fprintf(stdout, "    -w directory            Set the working directory\n\n");
   86 }
   87 
   88 
   89 /*
   90 ** USAGEERR - Print error message, program usage and then exit
   91 */
   92 static void
   93 usageerr(const char *progname, const char *fmt, ...)
   94 {
   95     char        buf[MAXLOGBUF];
   96     va_list     ap;
   97 
   98     /* Format err message */
   99     va_start(ap, fmt);
  100     (void) vsnprintf(buf, sizeof(buf), fmt, ap);
  101     va_end(ap);
  102 
  103     /* Print error message, program usage and then exit */
  104     (void) fprintf(stderr, "%s: %s\n", progname , buf);
  105     usage(progname);
  106     exit(EX_USAGE);
  107 }
  108 
  109 
  110 /*
  111 ** VERSIONINFO - Print program version info
  112 */
  113 static void
  114 versioninfo(const char *progname)
  115 {
  116     (void) fprintf(stdout, "%s %s\n", progname, VERSION);
  117 }
  118 
  119 
  120 /*
  121 ** MAIN - Main program loop
  122 */
  123 int
  124 main(int argc, char *argv[])
  125 {
  126     static      const char *args = "Bd:D:fhm:M:p:Pq:s:S:t:T:vw:";
  127 
  128     int         c, rstat;
  129     char       *p;
  130     const char *progname, *socket_name;
  131     FILE       *fp;
  132     struct      stat st;
  133     mode_t      save_umask;
  134     struct      sockaddr_un unix_addr;
  135 
  136     /* Program name */
  137     p = strrchr(argv[0], '/');
  138     if (p == NULL) {
  139         progname = argv[0];
  140     } else {
  141         progname = p + 1;
  142     }
  143 
  144     /* Open syslog */
  145     openlog(progname, LOG_PID, LOG_MAIL);
  146 
  147     /* Process command line options */
  148     while ((c = getopt(argc, argv, args)) != EOF) {
  149         switch (c) {
  150         case 'B':               /* use daemon_name policy bank */
  151             policybank_from_daemon_name = 1;
  152             break;
  153         case 'd':               /* debug level */
  154             if (optarg == NULL || *optarg == '\0') {
  155                 usageerr(progname, "option requires an argument -- %c",
  156                     (char)c);
  157             }
  158             debug_level = (int) strtol(optarg, &p, 10);
  159             if (p != NULL && *p != '\0') {
  160                 usageerr(progname, "debug level is not valid number: %s",
  161                     optarg);
  162             }
  163             if (debug_level < 0) {
  164                 usageerr(progname, "negative debug level: %d", debug_level);
  165             }
  166             debug_level += LOG_WARNING;
  167             break;
  168         case 'D':               /* delivery mechanism */
  169             if (optarg == NULL || *optarg == '\0') {
  170                 usageerr(progname, "option requires an argument -- %c",
  171                     (char)c);
  172             }
  173             if (strcmp(optarg, "client") != 0 &&
  174                 strcmp(optarg, "server") != 0)
  175             {
  176                 usageerr(progname, "unknown delivery mechanism '%s'", optarg);
  177             }
  178             delivery_care_of = optarg;
  179             break;
  180         case 'f':               /* run in foreground */
  181             daemonize = 0;
  182             break;
  183         case '?':               /* options parsing error */
  184             (void) fprintf(stderr, "\n");
  185         case 'h':               /* help */
  186             usage(progname);
  187             exit(EX_OK);
  188             break;
  189         case 'm':               /* maximum amavisd connections */
  190             max_conns = (int) strtol(optarg, &p, 10);
  191             if (p != NULL && *p != '\0') {
  192                 usageerr(progname,
  193                     "maximum amavisd connections is not valid number: %s",
  194                     optarg);
  195             }
  196             if (max_conns < 0) {
  197                 usageerr(progname, "negative maximum amavisd connections: %d",
  198                     max_conns);
  199             }
  200             break;
  201         case 'M':               /* maximum wait for connection */
  202             max_wait = (int) strtol(optarg, &p, 10);
  203             if (p != NULL && *p != '\0') {
  204                 usageerr(progname,
  205                     "maximum wait for connection is not valid number: %s",
  206                     optarg);
  207             }
  208             if (max_wait < 0) {
  209                 usageerr(progname, "negative maximum wait for connection: %d",
  210                     max_wait);
  211             }
  212             break;
  213         case 'p':               /* pid file name */
  214             if (optarg == NULL || *optarg == '\0') {
  215                 usageerr(progname, "option requires an argument -- %c",
  216                     (char)c);
  217             }
  218             pid_file = optarg;
  219             break;
  220         case 'P':               /* when amavisd fails mail will be passed */
  221             ignore_amavisd_error = 1;   /* through unchecked */
  222             break;
  223 #ifdef HAVE_SMFI_SETBACKLOG
  224         case 'q':               /* milter communication socket backlog */
  225             mlfi_socket_backlog = (int) strtol(optarg, &p, 10);
  226             if (p != NULL && *p != '\0') {
  227                 usageerr(progname,
  228                     "milter communication socket backlog is not valid number: "
  229                     "%s", optarg);
  230             }
  231             if (mlfi_socket_backlog < 0) {
  232                 usageerr(progname,
  233                     "negative milter communication socket backlog: %d",
  234                     mlfi_socket_backlog);
  235             }
  236             break;
  237 #endif
  238         case 's':               /* milter communication socket */
  239             if (optarg == NULL || *optarg == '\0') {
  240                 usageerr(progname, "option requires an argument -- %c",
  241                     (char)c);
  242             }
  243             if (strlen(optarg) >= sizeof(unix_addr.sun_path) - 1) {
  244                 usageerr(progname,
  245                     "milter communication socket name too long: %s", optarg);
  246             }
  247             mlfi_socket = optarg;
  248             break;
  249         case 't':               /* milter connection timeout */
  250             if (optarg == NULL || *optarg == '\0') {
  251                 usageerr(progname, "option requires an argument -- %c",
  252                     (char)c);
  253             }
  254             mlfi_timeout = (int) strtol(optarg, &p, 10);
  255             if (p != NULL && *p != '\0') {
  256                 usageerr(progname,
  257                     "milter connection timeout is not valid number: %s",
  258                     optarg);
  259             }
  260             if (mlfi_timeout < 0) {
  261                 usageerr(progname, "negative milter connection timeout: %ld",
  262                     mlfi_timeout);
  263             }
  264             break;
  265         case 'v':               /* version info */
  266             versioninfo(progname);
  267             exit(EX_OK);
  268             break;
  269         case 'w':               /* working directory */
  270             if (optarg == NULL || *optarg == '\0') {
  271                 usageerr(progname, "option requires an argument -- %c",
  272                     (char)c);
  273             }
  274             working_dir = optarg;
  275             break;
  276         case 'S':               /* amavisd communication socket */
  277             if (optarg == NULL || *optarg == '\0') {
  278                 usageerr(progname, "option requires an argument -- %c",
  279                     (char)c);
  280             }
  281             if (strlen(optarg) >= sizeof(unix_addr.sun_path) - 1) {
  282                 usageerr(progname,
  283                     "amavisd communication socket name too long: %s", optarg);
  284             }
  285             amavisd_socket = optarg;
  286             break;
  287         case 'T':               /* amavisd connection timeout */
  288             if (optarg == NULL || *optarg == '\0') {
  289                 usageerr(progname, "option requires an argument -- %c",
  290                     (char)c);
  291             }
  292             amavisd_timeout = (int) strtol(optarg, &p, 10);
  293             if (p != NULL && *p != '\0') {
  294                 usageerr(progname,
  295                     "amavisd connection timeout is not valid number: %s",
  296                     optarg);
  297             }
  298             if (amavisd_timeout < 0) {
  299                 usageerr(progname, "negative amavisd connection timeout: %ld",
  300                     amavisd_timeout);
  301             }
  302             break;
  303         default:                /* unknown option */
  304             usageerr(progname, "illegal option -- %c", (char)c);
  305             break;
  306         }
  307     }
  308 
  309     /* Create amavisd connections semaphore */
  310     if (max_conns > 0) {
  311         if (sem_init(&max_sem_t, 0, max_conns) == -1) {
  312             logmsg(LOG_ERR,
  313                 "could not initialize amavisd connections semaphore: %s",
  314                 strerror(errno));
  315             exit(EX_SOFTWARE);
  316         } else {
  317             max_sem = &max_sem_t;
  318         }
  319     }
  320 
  321     /* Check permissions on working directory */
  322     /* TODO: traverse working directory path */
  323     if (stat(working_dir, &st) != 0) {
  324         logmsg(LOG_ERR, "could not stat() to working directory %s: %s",
  325             working_dir, strerror(errno));
  326         exit(EX_SOFTWARE);
  327     }
  328     if (!S_ISDIR(st.st_mode)) {
  329         logmsg(LOG_ERR, "%s is not directory", working_dir);
  330         exit(EX_SOFTWARE);
  331     }
  332     if ((st.st_mode & S_IRWXO) != 0) {
  333         logmsg(LOG_ERR, "working directory %s is world accessible", working_dir);
  334         exit(EX_SOFTWARE);
  335     }
  336 
  337     /* Configure milter */
  338     socket_name = NULL;
  339     if (mlfi_socket[0] == '/') {
  340         socket_name = mlfi_socket;
  341     }
  342     if (! strncmp(mlfi_socket, "unix:", 5)) {
  343         socket_name = mlfi_socket + 5;
  344     }
  345     if (! strncmp(mlfi_socket, "local:", 6)) {
  346         socket_name = mlfi_socket + 6;
  347     }
  348     if (socket_name != NULL && unlink(socket_name) != 0 && errno != ENOENT) {
  349         logmsg(LOG_ERR, "could not unlink old milter socket %s: %s",
  350             socket_name, strerror(errno));
  351         exit(EX_SOFTWARE);
  352     }
  353     if (debug_level > LOG_DEBUG &&
  354         smfi_setdbg(debug_level - LOG_DEBUG) != MI_SUCCESS)
  355     {
  356         logmsg(LOG_ERR, "could not set milter debug level");
  357         exit(EX_SOFTWARE);
  358     }
  359     if (mlfi_timeout > 0 && smfi_settimeout(mlfi_timeout) != MI_SUCCESS) {
  360         logmsg(LOG_ERR, "could not set milter timeout");
  361         exit(EX_SOFTWARE);
  362     }
  363     if (smfi_setconn(mlfi_socket) != MI_SUCCESS) {
  364         logmsg(LOG_ERR, "could not set milter socket");
  365         exit(EX_SOFTWARE);
  366     }
  367     if (smfi_register(smfilter) != MI_SUCCESS) {
  368         logmsg(LOG_ERR, "could not register milter");
  369         exit(EX_SOFTWARE);
  370     }
  371 
  372     /* Unlink old pid file */
  373     if (pid_file != NULL) {
  374         if (unlink(pid_file) != 0 && errno != ENOENT) {
  375             logmsg(LOG_WARNING, "could not unlink old pid file %s: %s",
  376                 pid_file, strerror(errno));
  377         }
  378     }
  379 
  380     /* Connect to milter socket */
  381 #ifdef HAVE_SMFI_SETBACKLOG
  382     if (mlfi_socket_backlog > 0) {
  383         if (smfi_setbacklog(mlfi_socket_backlog) != MI_SUCCESS) {
  384             logmsg(LOG_WARNING, "could not set milter socket backlog to %d",
  385                 mlfi_socket_backlog);
  386         }
  387     }
  388 #endif
  389 #ifdef HAVE_SMFI_OPENSOCKET
  390     if (smfi_opensocket(false) != MI_SUCCESS) {
  391         logmsg(LOG_ERR, "could not open milter socket %s", mlfi_socket);
  392         exit(EX_SOFTWARE);
  393     }
  394 #endif
  395 
  396     /* Run in the background */
  397     if (daemonize) {
  398         if (daemon(1, 1) != -1) {
  399             daemonized = 1;
  400         } else {
  401             logmsg(LOG_ERR, "could not fork daemon process: %s",
  402                 strerror(errno));
  403             exit(EX_OSERR);
  404         }
  405     }
  406 
  407     /* Greetings message */
  408     logmsg(LOG_WARNING, "starting %s %s on socket %s", progname, VERSION,
  409         mlfi_socket);
  410 
  411     /* Create pid file */
  412     if (pid_file != NULL) {
  413         save_umask = umask(022);
  414         fp = fopen(pid_file, "w");
  415         if (fp == NULL) {
  416             logmsg(LOG_WARNING, "could not create pid file %s: %s",
  417                 pid_file, strerror(errno));
  418         } else {
  419             (void) fprintf(fp, "%ld\n", (long) getpid());
  420             if (ferror(fp)) {
  421                 logmsg(LOG_WARNING, "could not write to pid file %s: %s",
  422                     pid_file, strerror(errno));
  423                 clearerr(fp);
  424                 (void) fclose(fp);
  425             } else if (fclose(fp) != 0) {
  426                 logmsg(LOG_WARNING, "could not close pid file %s: %s",
  427                     pid_file, strerror(errno));
  428             }
  429         }
  430         umask(save_umask);
  431     }
  432 
  433     /* Run milter */
  434     if ((rstat = smfi_main()) != MI_SUCCESS) {
  435         logmsg(LOG_ERR, "%s failed", progname);
  436     } else {
  437         logmsg(LOG_WARNING, "stopping %s %s on socket %s", progname, VERSION,
  438             mlfi_socket);
  439     }
  440 
  441     /* Unlink pid file */
  442     if (pid_file != NULL) {
  443         if (unlink(pid_file) != 0) {
  444             logmsg(LOG_WARNING, "could not unlink pid file %s: %s",
  445                 pid_file, strerror(errno));
  446         }
  447     }
  448 
  449     /* Destroy amavisd connections semaphore */
  450     if (max_sem != NULL && sem_destroy(max_sem) == -1) {
  451         logmsg(errno == EBUSY ? LOG_ERR : LOG_WARNING,
  452             "%s: could not destroy amavisd connections semaphore: %s",
  453             progname, strerror(errno));
  454     }
  455 
  456     return rstat;
  457 }