"Fossies" - the Fresh Open Source Software Archive

Member "htpdate-1.3.4/htpdate.c" (18 Apr 2022, 29420 Bytes) of package /linux/www/htpdate-1.3.4.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 "htpdate.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.3.3_vs_1.3.4.

    1 /*
    2     htpdate v1.3.4
    3 
    4     Eddy Vervest <eddy@vervest.org>
    5     http://www.vervest.org/htp
    6 
    7     Synchronize local system with time offered by remote web servers
    8 
    9     This program works with the timestamps return by web servers,
   10     formatted as specified by HTTP/1.1 (RFC 2616, RFC 1123).
   11 
   12     Example usage:
   13 
   14     Debug mode (shows raw timestamps, round trip time (RTT) and
   15     time difference):
   16 
   17       htpdate -d www.example.com
   18 
   19     Adjust time smoothly:
   20 
   21       htpdate -a www.example.com
   22 
   23     ...see man page for more details
   24 
   25 
   26     This program is free software; you can redistribute it and/or
   27     modify it under the terms of the GNU General Public License
   28     as published by the Free Software Foundation; either version 2
   29     of the License, or (at your option) any later version.
   30     http://www.gnu.org/copyleft/gpl.html
   31 */
   32 
   33 /* Needed to avoid implicit warnings from strptime */
   34 #define _GNU_SOURCE
   35 
   36 #include <stdio.h>
   37 #include <stdlib.h>
   38 #include <unistd.h>
   39 #include <signal.h>
   40 #include <string.h>
   41 #include <sys/stat.h>
   42 #include <sys/types.h>
   43 #include <netinet/in.h>
   44 #include <sys/socket.h>
   45 #include <netdb.h>
   46 #include <time.h>
   47 #include <sys/time.h>
   48 #include <sys/timex.h>
   49 #include <syslog.h>
   50 #include <stdarg.h>
   51 #include <limits.h>
   52 #include <pwd.h>
   53 #include <grp.h>
   54 #include <float.h>
   55 
   56 #if defined __NetBSD__ || defined __FreeBSD__ || defined __APPLE__
   57 #define adjtimex ntp_adjtime
   58 #endif
   59 
   60 #ifdef ENABLE_HTTPS
   61 #include <openssl/ssl.h>
   62 #endif
   63 
   64 #define VERSION                  "1.3.4"
   65 #define MAX_HTTP_HOSTS           16                /* 16 web servers */
   66 #define DEFAULT_HTTP_PORT        "80"
   67 #define DEFAULT_PROXY_PORT       "8080"
   68 #define DEFAULT_IP_VERSION       PF_UNSPEC         /* IPv6 and IPv4 */
   69 #define DEFAULT_HTTP_VERSION     "1"               /* HTTP/1.1 */
   70 #define DEFAULT_TIME_LIMIT       31536000          /* 1 year */
   71 #define NO_TIME_LIMIT            -1
   72 #define ERR_TIMESTAMP            DBL_MAX          /* Err fetching date in getHTTPdate */
   73 #define DEFAULT_PRECISION        4                 /* 4 request per host */
   74 #define DEFAULT_MIN_SLEEP        1800              /* 30 minutes */
   75 #define DEFAULT_MAX_SLEEP        115200            /* 32 hours */
   76 #define MAX_DRIFT                32768000          /* 500 PPM */
   77 #define DEFAULT_PID_FILE         "/var/run/htpdate.pid"
   78 #define HEADREQUESTSIZE          1024
   79 #define URLSIZE                  128
   80 #define BUFFERSIZE               8192
   81 #define PRINTBUFFERSIZE          BUFFERSIZE
   82 
   83 #define sign(x) (x < 0 ? (-1) : 1)
   84 
   85 
   86 /* By default turn off "debug" and "log" mode  */
   87 static int debug   = 0;
   88 static int logmode = 0;
   89 static int verifycert = 0;
   90 
   91 
   92 /* Insertion sort is more efficient (and smaller) than qsort for small lists */
   93 static void insertsort(double a[], int length) {
   94     long i, j;
   95 
   96     for (i = 1; i < length; i++) {
   97         double value = a[i];
   98         for (j = i - 1; j >= 0 && a[j] > value; j--)
   99             a[j+1] = a[j];
  100         a[j+1] = value;
  101     }
  102 }
  103 
  104 
  105 /* Printlog is a slighty modified version from the one used in rdate */
  106 static void printlog(int is_error, char *format, ...) {
  107     va_list args;
  108     char buf[PRINTBUFFERSIZE];
  109 
  110     va_start(args, format);
  111     (void) vsnprintf(buf, sizeof(buf), format, args);
  112     va_end(args);
  113 
  114     if (logmode)
  115         syslog(is_error?LOG_WARNING:LOG_INFO, "%s", buf);
  116     else
  117         fprintf(is_error?stderr:stdout, "%s\n", buf);
  118 }
  119 
  120 
  121 /* Split argument in hostname/IP-address and TCP port
  122    Supports IPv6 literal addresses, RFC 2732.
  123 */
  124 static void splitURL(char **scheme, char **host, char **port, char **path) {
  125     char *rb, *rc, *lb, *lc, *ps;
  126 
  127     *path = "";
  128     *scheme = NULL;
  129     *port = DEFAULT_HTTP_PORT;
  130 
  131     if ((ps = strcasestr(*host, "https://")) != NULL) {
  132         #ifndef ENABLE_HTTPS
  133         printlog(1, "HTTPS not supported, %s", *host);
  134         return;
  135         #endif
  136         *scheme = "https://";
  137         *port = "443";
  138         *host = ps + 8;
  139     }
  140 
  141     if ((ps = strcasestr(*host, "http://")) != NULL) {
  142         *host = ps + 7;
  143     }
  144 
  145     lb = strchr(*host, '[');
  146     rb = strrchr(*host, ']');
  147     lc = strchr(*host, ':');
  148     rc = strrchr(*host, ':');
  149     ps = strchr(*host, '/');
  150 
  151     /* Extract URL path */
  152     if (ps != NULL) {
  153         ps[0] = '\0';
  154         *path = ps + 1;
  155     }
  156 
  157     /* A (literal) IPv6 address with portnumber */
  158     if (rb < rc && lb != NULL && rb != NULL) {
  159         rb[0] = '\0';
  160         *port = rc + 1;
  161         *host = lb + 1;
  162         return;
  163     }
  164 
  165     /* A (literal) IPv6 address without portnumber */
  166     if (rb != NULL && lb != NULL) {
  167         rb[0] = '\0';
  168         *host = lb + 1;
  169         return;
  170     }
  171 
  172     /* A IPv4 address or hostname with portnumber */
  173     if (rc != NULL && lc == rc) {
  174         rc[0] = '\0';
  175         *port = rc + 1;
  176         return;
  177     }
  178 }
  179 
  180 
  181 static void swuid(int id) {
  182     if (seteuid(id)) {
  183         printlog(1, "seteuid() %i", id);
  184         exit(1);
  185     }
  186 }
  187 
  188 
  189 static void swgid(int id) {
  190     if (setegid(id)) {
  191         printlog(1, "setegid() %i", id);
  192         exit(1);
  193     }
  194 }
  195 
  196 
  197 static long long getoffset(char remote_time[25]) {
  198     struct timeval  timevalue = {0, 0};
  199     struct timespec now;
  200     struct tm       tm;
  201 
  202     clock_gettime(CLOCK_REALTIME, &now);
  203     memset(&tm, 0, sizeof(struct tm));
  204     if (strptime(remote_time, "%d %b %Y %T", &tm) != NULL) {
  205         timevalue.tv_sec = timegm(&tm);
  206     } else {
  207         printlog(1, "unknown time format");
  208     }
  209     return now.tv_sec - timevalue.tv_sec;
  210 }
  211 
  212 
  213 static int sendHEAD(int server_s, char *headrequest, char *buffer) {
  214     int ret = send(server_s, headrequest, strlen(headrequest), 0);
  215 
  216     if (ret < 0) {
  217         printlog(1, "Error sending");
  218         return -1;
  219     }
  220 
  221     /* Receive data from the web server
  222        The return code from recv() is the number of bytes received
  223     */
  224     ret = recv(server_s, buffer, BUFFERSIZE - 1, 0) > 0;
  225 
  226     return ret;
  227 }
  228 
  229 
  230 #ifdef ENABLE_HTTPS
  231 static int sendHEADTLS(SSL *conn, char *headrequest, char *buffer) {
  232     int ret = SSL_write(conn, headrequest, strlen(headrequest));
  233 
  234     if (ret < 0) {
  235         printlog(1, "Error sending: %i", ret);
  236         return -1;
  237     }
  238 
  239     /* Receive data from the web server
  240        The return code is the number of bytes received
  241     */
  242     ret = SSL_read(conn, buffer, BUFFERSIZE - 1) > 0;
  243 
  244     return ret;
  245 }
  246 
  247 
  248 static int proxyCONNECT(
  249     int server_s,
  250     char *host, char *port,
  251     char *proxy, char *proxyport,
  252     char *httpversion) {
  253 
  254     char buffer[BUFFERSIZE] = {'\0'};
  255     char connectrequest[URLSIZE] = {'\0'};
  256 
  257     snprintf(connectrequest, URLSIZE,
  258        "CONNECT %s:%s HTTP/1.%s\r\n\r\n", host, port, httpversion);
  259 
  260     send(server_s, connectrequest, strlen(connectrequest), 0);
  261     int ret = recv(server_s, buffer, BUFFERSIZE - 1, 0) > 0;
  262     if (strstr(buffer, " 200 ") == NULL) {
  263         printlog(1, "Proxy error: %s:%s\r\n%s", proxy, proxyport, buffer);
  264     }
  265     return ret;
  266 }
  267 #endif
  268 
  269 
  270 static double getHTTPdate(
  271     char *scheme,
  272     char *host, char *port, char *path,
  273     char *proxy, char *proxyport,
  274     char *httpversion, int ipversion, int precision) {
  275 
  276     int                 server_s;
  277     int                 rc;
  278     int                 polls = 0;
  279     struct addrinfo     hints, *res;
  280     struct timespec     sleepspec, now;
  281     char                headrequest[HEADREQUESTSIZE] = {'\0'};
  282     char                buffer[BUFFERSIZE] = {'\0'};
  283     char                url[URLSIZE] = {'\0'};
  284     char                *pdate = NULL;
  285 
  286     /* Connect to web server via proxy server or directly */
  287     memset(&hints, 0, sizeof(hints));
  288     switch(ipversion) {
  289         case 4:                     /* IPv4 only */
  290             hints.ai_family = AF_INET;
  291             break;
  292         case 6:                     /* IPv6 only */
  293             hints.ai_family = AF_INET6;
  294             break;
  295         default:                    /* Support IPv6 and IPv4 name resolution */
  296             hints.ai_family = PF_UNSPEC;
  297     }
  298     hints.ai_socktype = SOCK_STREAM;
  299     hints.ai_flags = AI_CANONNAME;
  300 
  301     if (proxy == NULL) {
  302         rc = getaddrinfo(host, port, &hints, &res);
  303     } else {
  304         snprintf(url, URLSIZE, "http://%s:%s", host, port);
  305         rc = getaddrinfo(proxy, proxyport, &hints, &res);
  306     }
  307 
  308     /* Was the hostname and service resolvable? */
  309     if (rc) {
  310         printlog(1, "%s host or service unavailable", host);
  311         return(ERR_TIMESTAMP);
  312     }
  313 
  314     /* Build a combined HTTP/1.0 and 1.1 HEAD request
  315        Pragma: no-cache, "forces" an HTTP/1.0 and 1.1 compliant
  316        web server to return a fresh timestamp
  317        Connection: keep-alive, for multiple requests
  318     */
  319     snprintf(headrequest, HEADREQUESTSIZE,
  320         "HEAD %s/%s HTTP/1.%s\r\n"
  321         "Host: %s\r\n"
  322         "User-Agent: htpdate/"VERSION"\r\n"
  323         "Pragma: no-cache\r\n"
  324         "Cache-Control: no-cache\r\n"
  325         "Connection: keep-alive\r\n\r\n",
  326         url, path, httpversion, host);
  327 
  328     /* Loop through the available canonical names */
  329     do {
  330         server_s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
  331         if (server_s < 0) {
  332             continue;
  333         }
  334 
  335         rc = connect(server_s, res->ai_addr, res->ai_addrlen);
  336         if (rc) {
  337             close(server_s);
  338             server_s = -1;
  339             continue;
  340         }
  341 
  342         break;
  343     } while ((res = res->ai_next));
  344 
  345     freeaddrinfo(res);
  346 
  347     if (rc) {
  348         printlog(1, "%s connection failed", host);
  349         return(ERR_TIMESTAMP);
  350     }
  351 
  352     #ifdef ENABLE_HTTPS
  353     SSL_CTX *tls_ctx = SSL_CTX_new(TLS_method());
  354     SSL_CTX_set_default_verify_paths(tls_ctx);
  355     SSL_CTX_set_verify_depth(tls_ctx, 4);
  356     if (verifycert) SSL_CTX_set_verify(tls_ctx, SSL_VERIFY_PEER, NULL);
  357     SSL *conn = SSL_new(tls_ctx);
  358     if (scheme) {
  359         if (proxy) {
  360             rc = proxyCONNECT(server_s, host, port, proxy, proxyport, httpversion);
  361             if (rc != 1) {
  362                 printlog(1, "Proxy error: %i", rc);
  363                 return(ERR_TIMESTAMP);
  364             }
  365         }
  366         if (! SSL_set_fd(conn, server_s)) {
  367             printlog(1, "TLS error1");
  368             return(ERR_TIMESTAMP);
  369         } else {
  370            if (SSL_connect(conn) != 1) {
  371                printlog(1, "TLS error2");
  372                return(ERR_TIMESTAMP);
  373            }
  374         }
  375     }
  376     #endif
  377 
  378     long long offset = 0, first_offset = 0, prev_offset = 0;
  379     long nap = 1e9L;
  380     long when = nap >> precision;
  381     do {
  382         if (debug > 1)
  383             printlog(0, "bisect: %i, when: %09li", polls, when);
  384 
  385         /* Initialize timer */
  386         clock_gettime(CLOCK_REALTIME, &now);
  387 
  388         /* Initialize RTT (start of measurement) */
  389         long rtt = now.tv_sec;
  390 
  391         /* Wait till we reach the desired time, "when" */
  392         sleepspec.tv_sec = 0;
  393         if (when >= now.tv_nsec) {
  394             sleepspec.tv_nsec = when - now.tv_nsec;
  395         } else {
  396             sleepspec.tv_nsec = 1e9 + when - now.tv_nsec;
  397             rtt++;
  398         }
  399         nanosleep(&sleepspec, NULL);
  400 
  401         /* Send HEAD request */
  402         #ifdef ENABLE_HTTPS
  403         if (scheme)
  404             rc = sendHEADTLS(conn, headrequest, buffer);
  405         else
  406         #endif
  407             rc = sendHEAD(server_s, headrequest, buffer);
  408 
  409         if (!rc)
  410             printlog(1, "error from %s:%s", host, port );
  411 
  412         if (rc) {
  413             clock_gettime(CLOCK_REALTIME, &now);
  414 
  415             /* rtt contains round trip time in nanoseconds */
  416             rtt = (now.tv_sec - rtt) * 1e9 + now.tv_nsec - when;
  417 
  418             /* Look for the line that contains [dD]ate: */
  419             if ((pdate = strcasestr(buffer, "date: ")) != NULL && strlen(pdate) >= 35) {
  420 
  421                 if (debug > 2) printlog(0, "%s", buffer);
  422                 char remote_time[25] = {'\0'};
  423                 strncpy(remote_time, pdate + 11, 24);
  424 
  425                 polls++;
  426                 offset = getoffset(remote_time);
  427 
  428                 nap >>= 1;
  429                 if (polls > 1) {
  430                     if (offset != prev_offset) nap = -nap;
  431                 } else {
  432                     first_offset=offset;
  433                 }
  434                 prev_offset=offset;
  435                 /* Print host, raw timestamp, round trip time */
  436                 if (debug)
  437                     printlog(0, "%-25s %s, %s (%li ms) => %li", host, port,
  438                     remote_time, rtt / (long)1e6, offset);
  439             } else {
  440                 printlog(1, "%s no timestamp", host);
  441                 return(ERR_TIMESTAMP);
  442             }
  443         }                           /* bytes received */
  444         precision--;
  445         when += nap;
  446     } while (precision >= 1);
  447     close(server_s);
  448 
  449     #ifdef ENABLE_HTTPS
  450     if (scheme) SSL_shutdown(conn);
  451     SSL_CTX_free(tls_ctx);
  452     SSL_free(conn);
  453     #endif
  454 
  455     /* Rounding */
  456     if (debug) printlog(0, "when: %ld, nap: %ld", when, nap);
  457     if (when + nap == 1e9 && offset == 0) return 0;
  458 
  459     /* Return the time delta between web server time (timevalue)
  460        and system time (now)
  461     */
  462     if (first_offset < 0) {
  463         return(-first_offset + (1e9L-when)/(double)1e9L);
  464     } else {
  465         return(-first_offset + 1 - when/(double)1e9L);
  466     }
  467 }
  468 
  469 
  470 static int setclock(double timedelta, int setmode) {
  471     struct timespec now;
  472     struct timeval  timeofday;
  473     char   buffer[32] = {'\0'};
  474 
  475     if (timedelta == 0) {
  476         printlog(0, "No time correction needed");
  477         return(0);
  478     }
  479 
  480     switch (setmode) {
  481         case 0:                        /* No time adjustment, just print time */
  482             printlog(0, "Offset %.3f seconds", timedelta);
  483             return(0);
  484         case 1:                        /* Adjust time smoothly */
  485             timeofday.tv_sec  = (long)timedelta;
  486             timeofday.tv_usec = (long)((timedelta - timeofday.tv_sec) * 1e6);
  487 
  488             printlog(0, "Adjusting %.3f seconds", timedelta);
  489 
  490             /* Become root */
  491             swuid(0);
  492             return(adjtime(&timeofday, NULL));
  493         case 2:                        /* Set time */
  494             printlog(0, "Setting %.3f seconds", timedelta);
  495 
  496             clock_gettime(CLOCK_REALTIME, &now);
  497             timedelta += (now.tv_sec + now.tv_nsec * 1e-9);
  498 
  499             now.tv_sec  = (long)timedelta;
  500             now.tv_nsec = (long)(timedelta - now.tv_sec) * 1e9;
  501 
  502             strftime(buffer, sizeof(buffer), "%c", localtime(&now.tv_sec));
  503             printlog(0, "Set time: %s", buffer);
  504 
  505             /* Become root */
  506             swuid(0);
  507             return(clock_settime(CLOCK_REALTIME, &now));
  508         case 3:                        /* Set frequency, but first an adjust */
  509             return(setclock(timedelta, 1));
  510         default:
  511             return(-1);
  512     }
  513 }
  514 
  515 
  516 static int init_frequency(char *driftfile) {
  517     struct timex    tmx;
  518     FILE            *fp;
  519 
  520     fp = fopen(driftfile, "r");
  521     if (fp != NULL) {
  522         if (fscanf(fp, "%li", &tmx.freq)) {
  523             printlog(0, "Reading frequency from file %li", tmx.freq);
  524             fclose(fp);
  525         } else {
  526             printlog(1, "Error reading frequency from %s", driftfile);
  527             fclose(fp);
  528             return -1;
  529         }
  530     } else {
  531         printlog(1, "Error reading frequency from %s", driftfile);
  532         return -1;
  533     }
  534 
  535     if ((tmx.freq < -MAX_DRIFT) || (tmx.freq > MAX_DRIFT))
  536         tmx.freq = sign(tmx.freq) * MAX_DRIFT;
  537 
  538     printlog(0, "Set frequency: %li", tmx.freq);
  539     tmx.modes = MOD_FREQUENCY;
  540 
  541     /* Become root */
  542     swuid(0);
  543     return(adjtimex(&tmx));
  544 }
  545 
  546 
  547 static int htpdate_adjtimex(double drift, char *driftfile) {
  548     struct timex    tmx;
  549     long            freq;
  550     FILE            *fp;
  551 
  552     /* Read current clock frequency */
  553     tmx.modes = 0;
  554     adjtimex(&tmx);
  555 
  556     /* Calculate new frequency */
  557     freq = (long)(65536e6 * drift);
  558 
  559     /* Weighted average of current and new frequency */
  560     tmx.freq = tmx.freq + (freq >> 3);
  561     if ((tmx.freq < -MAX_DRIFT) || (tmx.freq > MAX_DRIFT))
  562         tmx.freq = sign(tmx.freq) * MAX_DRIFT;
  563 
  564     printlog(0, "Set frequency %li", tmx.freq);
  565     tmx.modes = MOD_FREQUENCY;
  566 
  567     if (driftfile) {
  568        fp = fopen(driftfile, "w");
  569        if (fp != NULL) {
  570            printlog(0, "Update %s", driftfile);
  571            fprintf(fp, "%li", tmx.freq);
  572            fclose(fp);
  573        } else {
  574            printlog(1, "Error writing frequency to %s", driftfile);
  575        }
  576     }
  577 
  578     /* Become root */
  579     swuid(0);
  580     return(adjtimex(&tmx));
  581 }
  582 
  583 
  584 static void showhelp() {
  585     puts("htpdate version "VERSION"\n\
  586 Usage: htpdate [-046acdhlnqstvxD] [-f driftfile] [-i pidfile] [-m minpoll]\n\
  587          [-M maxpoll] [-p precision] [-P <proxyserver>[:port]]\n\
  588          [-u user[:group]] <URL> ...\n\n\
  589   -0    HTTP/1.0 request\n\
  590   -4    Force IPv4 name resolution only\n\
  591   -6    Force IPv6 name resolution only\n\
  592   -a    adjust time smoothly\n\
  593   -c    verify server certificate\n\
  594   -d    debug mode\n\
  595   -D    daemon mode\n\
  596   -f    drift/frequency file\n\
  597   -F    run daemon in foreground\n\
  598   -h    help\n\
  599   -i    pidfile\n\
  600   -l    use syslog for output\n\
  601   -m    minimum poll interval\n\
  602   -M    maximum poll interval\n\
  603   -n    no proxy (ignore http_proxy environment variable)\n\
  604   -p    precision (1..9, default 4)\n\
  605   -P    proxy server\n\
  606   -q    query only, don't make time changes (default)\n\
  607   -s    set time\n\
  608   -t    turn off sanity time check\n\
  609   -u    run daemon as user\n\
  610   -v    version\n\
  611   -x    adjust system clock frequency\n\
  612   URL   one of more URLs (max. 16), e.g. www.example.com\n");
  613 
  614     return;
  615 }
  616 
  617 
  618 /* Run htpdate in daemon mode */
  619 static void runasdaemon(char *pidfile) {
  620     FILE  *pid_file;
  621     pid_t pid;
  622 
  623     /* Check if htpdate is already running (pid exists)*/
  624     pid_file = fopen(pidfile, "r");
  625     if (pid_file) {
  626         fputs("htpdate already running\n", stderr);
  627         exit(1);
  628     }
  629 
  630     pid = fork();
  631     if (pid < 0) {
  632         fputs("fork()\n", stderr);
  633         exit(1);
  634     }
  635 
  636     if (pid > 0) exit(0);
  637 
  638     /* Create a new SID for the child process */
  639     if (setsid () < 0) exit(1);
  640 
  641     /* Close out the standard file descriptors */
  642     close(STDIN_FILENO);
  643     close(STDOUT_FILENO);
  644     close(STDERR_FILENO);
  645 
  646     signal(SIGHUP, SIG_IGN);
  647 
  648     /* Change the file mode mask */
  649     umask(0);
  650 
  651     /* Change the current working directory */
  652     if (chdir("/") < 0) {
  653         printlog(1, "chdir()");
  654         exit(1);
  655     }
  656 
  657     /* Second fork, to become the grandchild */
  658     pid = fork();
  659     if (pid < 0) {
  660         printlog(1, "fork()");
  661         exit(1);
  662     }
  663 
  664     if (pid > 0) {
  665         /* Write a pid file */
  666         pid_file = fopen(pidfile, "w");
  667         if (!pid_file) {
  668             printlog(1, "Error writing pid file");
  669             exit(1);
  670         } else {
  671             fprintf(pid_file, "%d\n", pid);
  672             fclose(pid_file);
  673         }
  674         exit(0);
  675     }
  676 }
  677 
  678 
  679 int main(int argc, char *argv[]) {
  680     char            *host = NULL, *proxy = NULL, *proxyport = NULL;
  681     char            *port = NULL;
  682     char            *path = NULL;
  683     char            *scheme = NULL;
  684     char            *httpversion = DEFAULT_HTTP_VERSION;
  685     char            *pidfile = DEFAULT_PID_FILE;
  686     char            *user = NULL, *userstr = NULL, *group = NULL;
  687     double          timeavg, drift = 0;
  688     double          timedelta[MAX_HTTP_HOSTS-1];
  689     int             numservers;
  690     int             precision = DEFAULT_PRECISION;
  691     int             setmode = 0;
  692     int             i, param;
  693     int             daemonize = 0, foreground = 0;
  694     int             noproxyenv = 0;
  695     int             ipversion = DEFAULT_IP_VERSION;
  696     long long       timelimit = DEFAULT_TIME_LIMIT;
  697     int             minsleep = DEFAULT_MIN_SLEEP;
  698     int             maxsleep = DEFAULT_MAX_SLEEP;
  699     int             sleeptime = minsleep;
  700     int             sw_uid = 0, sw_gid = 0;
  701     time_t          starttime = 0;
  702 
  703     struct passwd   *pw;
  704     struct group    *gr;
  705 
  706     extern char     *optarg;
  707     extern int      optind;
  708 
  709     char            *driftfile = NULL;
  710 
  711     /* Parse the command line switches and arguments */
  712     while ((param = getopt(argc, argv, "046acdf:hi:lm:np:qstu:vxDFM:P:")) != -1)
  713     switch(param) {
  714         case '0':               /* HTTP/1.0 */
  715             httpversion = "0";
  716             break;
  717         case '4':               /* IPv4 only */
  718             ipversion = 4;
  719             break;
  720         case '6':               /* IPv6 only */
  721             ipversion = 6;
  722             break;
  723         case 'a':               /* adjust time */
  724             setmode = 1;
  725             break;
  726         case 'c':               /* server certificat verification */
  727             verifycert = 1;
  728             break;
  729         case 'd':               /* turn debug on */
  730             if (debug <= 3) debug++;
  731             break;
  732         case 'f':               /* drift file */
  733             driftfile = (char *)optarg;
  734             init_frequency(driftfile);
  735             break;
  736         case 'h':               /* show help */
  737             showhelp();
  738             exit(0);
  739         case 'i':               /* pid file */
  740             pidfile = (char *)optarg;
  741             break;
  742         case 'l':               /* log mode */
  743             logmode = 1;
  744             break;
  745         case 'm':               /* minimum poll interval */
  746             if ((minsleep = atoi(optarg)) <= 0) {
  747                 fputs("Invalid sleep time\n", stderr);
  748                 exit(1);
  749             }
  750             sleeptime = minsleep;
  751             break;
  752         case 'n':               /* don't get proxy from environment */
  753             noproxyenv = 1;
  754             break;
  755         case 'p':               /* precision */
  756             precision = atoi(optarg) ;
  757             if ((precision < 1) || (precision > 9)) {
  758                 fputs("Invalid precision\n", stderr);
  759                 exit(1);
  760             }
  761             break;
  762         case 'q':               /* query only (default) */
  763             break;
  764         case 's':               /* set time */
  765             setmode = 2;
  766             break;
  767         case 't':               /* disable "sanity" time check */
  768             timelimit = NO_TIME_LIMIT;
  769             break;
  770         case 'u':               /* drop root privileges and run as user */
  771             user = (char *)optarg;
  772             userstr = strchr(user, ':');
  773             if (userstr != NULL) {
  774                 userstr[0] = '\0';
  775                 group = userstr + 1;
  776             }
  777             if ((pw = getpwnam(user)) != NULL) {
  778                 sw_uid = pw->pw_uid;
  779                 sw_gid = pw->pw_gid;
  780             } else {
  781                 printf("Unknown user %s\n", user);
  782                 exit(1);
  783             }
  784             if (group != NULL) {
  785                 if ((gr = getgrnam(group)) != NULL) {
  786                     sw_gid = gr->gr_gid;
  787                 } else {
  788                     printf("Unknown group %s\n", group);
  789                     exit(1);
  790                 }
  791             }
  792             break;
  793         case 'v':               /* print version */
  794             printf("htpdate version %s\n", VERSION);
  795             exit(0);
  796         case 'x':               /* adjust time and clock frequency */
  797             setmode = 3;
  798             break;
  799         case 'D':               /* run as daemon */
  800             daemonize = 1;
  801             logmode = 1;
  802             break;
  803         case 'F':               /* run daemon in foreground, don't fork */
  804             foreground = 1;
  805             break;
  806         case 'M':               /* maximum poll interval */
  807             if ((maxsleep = atoi(optarg)) <= 0) {
  808                 fputs("Invalid sleep time\n", stderr);
  809                 exit(1);
  810             }
  811             break;
  812         case 'P':
  813             proxy = (char *)optarg;
  814             proxyport = DEFAULT_PROXY_PORT;
  815             splitURL(&scheme, &proxy, &proxyport, &path);
  816             break;
  817         case '?':
  818             return 1;
  819         default:
  820             abort();
  821     }
  822 
  823     /* Display help page, if no servers are specified */
  824     if (argv[optind] == NULL) {
  825         showhelp();
  826         exit(1);
  827     }
  828 
  829     /* Exit if too many servers are specified */
  830     numservers = argc - optind;
  831     if (numservers > MAX_HTTP_HOSTS) {
  832         fputs("Too many servers\n", stderr);
  833         exit(1);
  834     }
  835 
  836     /* Use http_proxy environment variable */
  837     if (getenv("http_proxy") && !noproxyenv) {
  838         if ((proxy = strstr(getenv("http_proxy"), "http://")) == NULL) {
  839             printlog(1, "Invalid proxy specified: %s", getenv("http_proxy"));
  840             exit(1);
  841         }
  842         if (debug) printlog(0, "Proxy: %s", proxy);
  843         proxy += 7;
  844         splitURL(&scheme, &proxy, &proxyport, &path);
  845     }
  846 
  847     /* One must be "root" to change the system time */
  848     if ((getuid() != 0) && (setmode || daemonize || foreground)) {
  849         fputs("Only root can change time\n", stderr);
  850         exit(1);
  851     }
  852 
  853     /* Run as a daemonize when -D is set */
  854     if (daemonize) {
  855         runasdaemon(pidfile);
  856     }
  857 
  858     /* Query only mode doesn't exist in daemon or foreground mode */
  859     if (daemonize || foreground) {
  860         printlog(0, "htpdate version "VERSION" started");
  861         if (!setmode) setmode = 1;
  862     }
  863 
  864     /* Now we are root, we drop the privileges (if specified) */
  865     if (sw_gid) swgid(sw_gid);
  866     if (sw_uid) swuid(sw_uid);
  867 
  868     #ifdef ENABLE_HTTPS
  869     SSL_library_init();
  870     #endif
  871 
  872     /* Infinite poll cycle loop in daemonize or foreground mode */
  873     do {
  874 
  875         /* Initialize number of received valid timestamps, good timestamps
  876            and the average of the good timestamps
  877         */
  878         int    validtimes = 0, goodtimes = 0;
  879         double sumtimes = 0, mean = 0;
  880 
  881         /* Loop through the time sources (web servers); poll cycle */
  882         for (i = optind; i < argc; i++) {
  883 
  884             /* host:port is stored in argv[i] */
  885             char *hostport = strdup(argv[i]);
  886             host = hostport;
  887             splitURL(&scheme, &host, &port, &path);
  888 
  889             double offset = getHTTPdate(scheme, host, port, path,
  890                 proxy, proxyport, httpversion, ipversion, precision);
  891             free(hostport);
  892             if (debug && offset != ERR_TIMESTAMP) {
  893                 printlog(0, "offset: %.6f", offset);
  894             }
  895 
  896             /* Only include valid responses in timedelta[] */
  897             if ((timelimit == NO_TIME_LIMIT && offset != ERR_TIMESTAMP) || (offset < timelimit && offset > -timelimit)) {
  898                 timedelta[validtimes] = offset;
  899                 validtimes++;
  900             }
  901         }
  902 
  903         /* Sort the timedelta results */
  904         insertsort(timedelta, validtimes);
  905 
  906         /* Mean time value */
  907         mean = timedelta[validtimes/2];
  908 
  909         /* Filter out the bogus timevalues. A timedelta which is more than
  910            1 second off from mean, is considered a 'false ticker'.
  911            NTP synced web servers can never be more off than a second.
  912         */
  913         for (i = 0; i < validtimes; i++) {
  914             if ((timedelta[i]-mean) < .5 && (timedelta[i]-mean) > -.5) {
  915                 sumtimes += timedelta[i];
  916                 goodtimes++;
  917             }
  918         }
  919 
  920         /* Check if we have at least one valid response */
  921         if (goodtimes) {
  922 
  923             timeavg = sumtimes / goodtimes;
  924 
  925             if (debug > 1)
  926                 printlog(0, "#: %d, mean: %.3f, average: %.3f", goodtimes, mean, timeavg);
  927 
  928             /* Do I really need to change the time?  */
  929             if (sumtimes || !(daemonize || foreground)) {
  930                 if (setclock(timeavg, setmode) < 0)
  931                     printlog(1, "Time change failed");
  932 
  933                 /* Drop root privileges again */
  934                 if (sw_uid) swuid(sw_uid);
  935 
  936                 if (daemonize || foreground) {
  937                     if (starttime) {
  938                         /* Calculate systematic clock drift */
  939                         drift = timeavg / (time(NULL) - starttime);
  940                         printlog(0, "Drift %.2f PPM, %.2f s/day", drift*1e6, drift*86400);
  941 
  942                         /* Adjust system clock */
  943                         if (setmode == 3) {
  944                             starttime = time(NULL);
  945                             /* Adjust the clock frequency */
  946                             if (htpdate_adjtimex(drift, driftfile) < 0)
  947                                 printlog(1, "Frequency change failed");
  948 
  949                             /* Drop root privileges again */
  950                             if (sw_uid) swuid(sw_uid);
  951                         }
  952                     } else {
  953                         starttime = time(NULL);
  954                     }
  955 
  956                     /* Decrease polling interval to minimum */
  957                     sleeptime = minsleep;
  958 
  959                     /* Sleep for 30 minutes after a time adjust or set */
  960                     sleep(DEFAULT_MIN_SLEEP);
  961                 }
  962             } else {
  963                 /* Increase polling interval */
  964                 if (sleeptime < maxsleep) sleeptime <<= 1;
  965             }
  966 
  967             if (daemonize || foreground) {
  968                 printlog(0, "sleep for %ld s", sleeptime);
  969                 sleep(sleeptime);
  970             }
  971 
  972         } else {
  973             printlog(1, "No server suitable for synchronization found");
  974             /* Sleep for minsleep to avoid flooding */
  975             if (daemonize || foreground)
  976                 sleep(minsleep);
  977             else
  978                 exit(1);
  979         }
  980 
  981         /* After first poll cycle do not step through time, only adjust */
  982         if (setmode != 3) setmode = 1;
  983 
  984     } while (daemonize || foreground);         /* end of infinite while loop */
  985 
  986     exit(0);
  987 }