"Fossies" - the Fresh Open Source Software Archive

Member "chrony-3.5/main.c" (10 May 2019, 14513 Bytes) of package /linux/misc/chrony-3.5.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 and the last Fossies "Diffs" side-by-side code changes report: 3.3_vs_3.4.

    1 /*
    2   chronyd/chronyc - Programs for keeping computer clocks accurate.
    3 
    4  **********************************************************************
    5  * Copyright (C) Richard P. Curnow  1997-2003
    6  * Copyright (C) John G. Hasler  2009
    7  * Copyright (C) Miroslav Lichvar  2012-2018
    8  * 
    9  * This program is free software; you can redistribute it and/or modify
   10  * it under the terms of version 2 of the GNU General Public License as
   11  * published by the Free Software Foundation.
   12  * 
   13  * This program is distributed in the hope that it will be useful, but
   14  * WITHOUT ANY WARRANTY; without even the implied warranty of
   15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   16  * General Public License for more details.
   17  * 
   18  * You should have received a copy of the GNU General Public License along
   19  * with this program; if not, write to the Free Software Foundation, Inc.,
   20  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
   21  * 
   22  **********************************************************************
   23 
   24   =======================================================================
   25 
   26   The main program
   27   */
   28 
   29 #include "config.h"
   30 
   31 #include "sysincl.h"
   32 
   33 #include "main.h"
   34 #include "sched.h"
   35 #include "local.h"
   36 #include "sys.h"
   37 #include "ntp_io.h"
   38 #include "ntp_signd.h"
   39 #include "ntp_sources.h"
   40 #include "ntp_core.h"
   41 #include "sources.h"
   42 #include "sourcestats.h"
   43 #include "reference.h"
   44 #include "logging.h"
   45 #include "conf.h"
   46 #include "cmdmon.h"
   47 #include "keys.h"
   48 #include "manual.h"
   49 #include "rtc.h"
   50 #include "refclock.h"
   51 #include "clientlog.h"
   52 #include "nameserv.h"
   53 #include "privops.h"
   54 #include "smooth.h"
   55 #include "tempcomp.h"
   56 #include "util.h"
   57 
   58 /* ================================================== */
   59 
   60 /* Set when the initialisation chain has been completed.  Prevents finalisation
   61  * chain being run if a fatal error happened early. */
   62 
   63 static int initialised = 0;
   64 
   65 static int exit_status = 0;
   66 
   67 static int reload = 0;
   68 
   69 static REF_Mode ref_mode = REF_ModeNormal;
   70 
   71 /* ================================================== */
   72 
   73 static void
   74 do_platform_checks(void)
   75 {
   76   /* Require at least 32-bit integers, two's complement representation and
   77      the usual implementation of conversion of unsigned integers */
   78   assert(sizeof (int) >= 4);
   79   assert(-1 == ~0);
   80   assert((int32_t)4294967295U == (int32_t)-1);
   81 }
   82 
   83 /* ================================================== */
   84 
   85 static void
   86 delete_pidfile(void)
   87 {
   88   const char *pidfile = CNF_GetPidFile();
   89 
   90   if (!pidfile[0])
   91     return;
   92 
   93   /* Don't care if this fails, there's not a lot we can do */
   94   unlink(pidfile);
   95 }
   96 
   97 /* ================================================== */
   98 
   99 void
  100 MAI_CleanupAndExit(void)
  101 {
  102   if (!initialised) exit(exit_status);
  103   
  104   if (CNF_GetDumpDir()[0] != '\0') {
  105     SRC_DumpSources();
  106   }
  107 
  108   /* Don't update clock when removing sources */
  109   REF_SetMode(REF_ModeIgnore);
  110 
  111   SMT_Finalise();
  112   TMC_Finalise();
  113   MNL_Finalise();
  114   CLG_Finalise();
  115   NSD_Finalise();
  116   NSR_Finalise();
  117   SST_Finalise();
  118   NCR_Finalise();
  119   NIO_Finalise();
  120   CAM_Finalise();
  121   KEY_Finalise();
  122   RCL_Finalise();
  123   SRC_Finalise();
  124   REF_Finalise();
  125   RTC_Finalise();
  126   SYS_Finalise();
  127   SCH_Finalise();
  128   LCL_Finalise();
  129   PRV_Finalise();
  130 
  131   delete_pidfile();
  132   
  133   CNF_Finalise();
  134   HSH_Finalise();
  135   LOG_Finalise();
  136 
  137   exit(exit_status);
  138 }
  139 
  140 /* ================================================== */
  141 
  142 static void
  143 signal_cleanup(int x)
  144 {
  145   if (!initialised) exit(0);
  146   SCH_QuitProgram();
  147 }
  148 
  149 /* ================================================== */
  150 
  151 static void
  152 quit_timeout(void *arg)
  153 {
  154   /* Return with non-zero status if the clock is not synchronised */
  155   exit_status = REF_GetOurStratum() >= NTP_MAX_STRATUM;
  156   SCH_QuitProgram();
  157 }
  158 
  159 /* ================================================== */
  160 
  161 static void
  162 ntp_source_resolving_end(void)
  163 {
  164   NSR_SetSourceResolvingEndHandler(NULL);
  165 
  166   if (reload) {
  167     /* Note, we want reload to come well after the initialisation from
  168        the real time clock - this gives us a fighting chance that the
  169        system-clock scale for the reloaded samples still has a
  170        semblence of validity about it. */
  171     SRC_ReloadSources();
  172   }
  173 
  174   SRC_RemoveDumpFiles();
  175   RTC_StartMeasurements();
  176   RCL_StartRefclocks();
  177   NSR_StartSources();
  178   NSR_AutoStartSources();
  179 
  180   /* Special modes can end only when sources update their reachability.
  181      Give up immediatelly if there are no active sources. */
  182   if (ref_mode != REF_ModeNormal && !SRC_ActiveSources()) {
  183     REF_SetUnsynchronised();
  184   }
  185 }
  186 
  187 /* ================================================== */
  188 
  189 static void
  190 post_init_ntp_hook(void *anything)
  191 {
  192   if (ref_mode == REF_ModeInitStepSlew) {
  193     /* Remove the initstepslew sources and set normal mode */
  194     NSR_RemoveAllSources();
  195     ref_mode = REF_ModeNormal;
  196     REF_SetMode(ref_mode);
  197   }
  198 
  199   /* Close the pipe to the foreground process so it can exit */
  200   LOG_CloseParentFd();
  201 
  202   CNF_AddSources();
  203   CNF_AddBroadcasts();
  204 
  205   NSR_SetSourceResolvingEndHandler(ntp_source_resolving_end);
  206   NSR_ResolveSources();
  207 }
  208 
  209 /* ================================================== */
  210 
  211 static void
  212 reference_mode_end(int result)
  213 {
  214   switch (ref_mode) {
  215     case REF_ModeNormal:
  216     case REF_ModeUpdateOnce:
  217     case REF_ModePrintOnce:
  218       exit_status = !result;
  219       SCH_QuitProgram();
  220       break;
  221     case REF_ModeInitStepSlew:
  222       /* Switch to the normal mode, the delay is used to prevent polling
  223          interval shorter than the burst interval if some configured servers
  224          were used also for initstepslew */
  225       SCH_AddTimeoutByDelay(2.0, post_init_ntp_hook, NULL);
  226       break;
  227     default:
  228       assert(0);
  229   }
  230 }
  231 
  232 /* ================================================== */
  233 
  234 static void
  235 post_init_rtc_hook(void *anything)
  236 {
  237   if (CNF_GetInitSources() > 0) {
  238     CNF_AddInitSources();
  239     NSR_StartSources();
  240     assert(REF_GetMode() != REF_ModeNormal);
  241     /* Wait for mode end notification */
  242   } else {
  243     (post_init_ntp_hook)(NULL);
  244   }
  245 }
  246 
  247 /* ================================================== */
  248 
  249 static void
  250 check_pidfile(void)
  251 {
  252   const char *pidfile = CNF_GetPidFile();
  253   FILE *in;
  254   int pid, count;
  255   
  256   in = fopen(pidfile, "r");
  257   if (!in)
  258     return;
  259 
  260   count = fscanf(in, "%d", &pid);
  261   fclose(in);
  262   
  263   if (count != 1)
  264     return;
  265 
  266   if (getsid(pid) < 0)
  267     return;
  268 
  269   LOG_FATAL("Another chronyd may already be running (pid=%d), check %s",
  270             pid, pidfile);
  271 }
  272 
  273 /* ================================================== */
  274 
  275 static void
  276 write_pidfile(void)
  277 {
  278   const char *pidfile = CNF_GetPidFile();
  279   FILE *out;
  280 
  281   if (!pidfile[0])
  282     return;
  283 
  284   out = fopen(pidfile, "w");
  285   if (!out) {
  286     LOG_FATAL("Could not open %s : %s", pidfile, strerror(errno));
  287   } else {
  288     fprintf(out, "%d\n", (int)getpid());
  289     fclose(out);
  290   }
  291 }
  292 
  293 /* ================================================== */
  294 
  295 #define DEV_NULL "/dev/null"
  296 
  297 static void
  298 go_daemon(void)
  299 {
  300   int pid, fd, pipefd[2];
  301 
  302   /* Create pipe which will the daemon use to notify the grandparent
  303      when it's initialised or send an error message */
  304   if (pipe(pipefd)) {
  305     LOG_FATAL("pipe() failed : %s", strerror(errno));
  306   }
  307 
  308   /* Does this preserve existing signal handlers? */
  309   pid = fork();
  310 
  311   if (pid < 0) {
  312     LOG_FATAL("fork() failed : %s", strerror(errno));
  313   } else if (pid > 0) {
  314     /* In the 'grandparent' */
  315     char message[1024];
  316     int r;
  317 
  318     close(pipefd[1]);
  319     r = read(pipefd[0], message, sizeof (message));
  320     if (r) {
  321       if (r > 0) {
  322         /* Print the error message from the child */
  323         message[sizeof (message) - 1] = '\0';
  324         fprintf(stderr, "%s\n", message);
  325       }
  326       exit(1);
  327     } else
  328       exit(0);
  329   } else {
  330     close(pipefd[0]);
  331 
  332     setsid();
  333 
  334     /* Do 2nd fork, as-per recommended practice for launching daemons. */
  335     pid = fork();
  336 
  337     if (pid < 0) {
  338       LOG_FATAL("fork() failed : %s", strerror(errno));
  339     } else if (pid > 0) {
  340       exit(0); /* In the 'parent' */
  341     } else {
  342       /* In the child we want to leave running as the daemon */
  343 
  344       /* Change current directory to / */
  345       if (chdir("/") < 0) {
  346         LOG_FATAL("chdir() failed : %s", strerror(errno));
  347       }
  348 
  349       /* Don't keep stdin/out/err from before. But don't close
  350          the parent pipe yet. */
  351       for (fd=0; fd<1024; fd++) {
  352         if (fd != pipefd[1])
  353           close(fd);
  354       }
  355 
  356       LOG_SetParentFd(pipefd[1]);
  357 
  358       /* Open /dev/null as new stdin/out/err */
  359       errno = 0;
  360       if (open(DEV_NULL, O_RDONLY) != STDIN_FILENO ||
  361           open(DEV_NULL, O_WRONLY) != STDOUT_FILENO ||
  362           open(DEV_NULL, O_RDWR) != STDERR_FILENO)
  363         LOG_FATAL("Could not open %s : %s", DEV_NULL, strerror(errno));
  364     }
  365   }
  366 }
  367 
  368 /* ================================================== */
  369 
  370 static void
  371 print_help(const char *progname)
  372 {
  373       printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-t TIMEOUT] [-f FILE|COMMAND...]\n",
  374              progname);
  375 }
  376 
  377 /* ================================================== */
  378 
  379 static void
  380 print_version(void)
  381 {
  382   printf("chronyd (chrony) version %s (%s)\n", CHRONY_VERSION, CHRONYD_FEATURES);
  383 }
  384 
  385 /* ================================================== */
  386 
  387 static int
  388 parse_int_arg(const char *arg)
  389 {
  390   int i;
  391 
  392   if (sscanf(arg, "%d", &i) != 1)
  393     LOG_FATAL("Invalid argument %s", arg);
  394   return i;
  395 }
  396 
  397 /* ================================================== */
  398 
  399 int main
  400 (int argc, char **argv)
  401 {
  402   const char *conf_file = DEFAULT_CONF_FILE;
  403   const char *progname = argv[0];
  404   char *user = NULL, *log_file = NULL;
  405   struct passwd *pw;
  406   int opt, debug = 0, nofork = 0, address_family = IPADDR_UNSPEC;
  407   int do_init_rtc = 0, restarted = 0, client_only = 0, timeout = 0;
  408   int scfilter_level = 0, lock_memory = 0, sched_priority = 0;
  409   int clock_control = 1, system_log = 1;
  410   int config_args = 0;
  411 
  412   do_platform_checks();
  413 
  414   LOG_Initialise();
  415 
  416   /* Parse (undocumented) long command-line options */
  417   for (optind = 1; optind < argc; optind++) {
  418     if (!strcmp("--help", argv[optind])) {
  419       print_help(progname);
  420       return 0;
  421     } else if (!strcmp("--version", argv[optind])) {
  422       print_version();
  423       return 0;
  424     }
  425   }
  426 
  427   optind = 1;
  428 
  429   /* Parse short command-line options */
  430   while ((opt = getopt(argc, argv, "46df:F:hl:mnP:qQrRst:u:vx")) != -1) {
  431     switch (opt) {
  432       case '4':
  433       case '6':
  434         address_family = opt == '4' ? IPADDR_INET4 : IPADDR_INET6;
  435         break;
  436       case 'd':
  437         debug++;
  438         nofork = 1;
  439         system_log = 0;
  440         break;
  441       case 'f':
  442         conf_file = optarg;
  443         break;
  444       case 'F':
  445         scfilter_level = parse_int_arg(optarg);
  446         break;
  447       case 'l':
  448         log_file = optarg;
  449         break;
  450       case 'm':
  451         lock_memory = 1;
  452         break;
  453       case 'n':
  454         nofork = 1;
  455         break;
  456       case 'P':
  457         sched_priority = parse_int_arg(optarg);
  458         break;
  459       case 'q':
  460         ref_mode = REF_ModeUpdateOnce;
  461         nofork = 1;
  462         client_only = 0;
  463         system_log = 0;
  464         break;
  465       case 'Q':
  466         ref_mode = REF_ModePrintOnce;
  467         nofork = 1;
  468         client_only = 1;
  469         clock_control = 0;
  470         system_log = 0;
  471         break;
  472       case 'r':
  473         reload = 1;
  474         break;
  475       case 'R':
  476         restarted = 1;
  477         break;
  478       case 's':
  479         do_init_rtc = 1;
  480         break;
  481       case 't':
  482         timeout = parse_int_arg(optarg);
  483         break;
  484       case 'u':
  485         user = optarg;
  486         break;
  487       case 'v':
  488         print_version();
  489         return 0;
  490       case 'x':
  491         clock_control = 0;
  492         break;
  493       default:
  494         print_help(progname);
  495         return opt != 'h';
  496     }
  497   }
  498 
  499   if (getuid() && !client_only)
  500     LOG_FATAL("Not superuser");
  501 
  502   /* Turn into a daemon */
  503   if (!nofork) {
  504     go_daemon();
  505   }
  506 
  507   if (log_file) {
  508     LOG_OpenFileLog(log_file);
  509   } else if (system_log) {
  510     LOG_OpenSystemLog();
  511   }
  512   
  513   LOG_SetDebugLevel(debug);
  514   
  515   LOG(LOGS_INFO, "chronyd version %s starting (%s)", CHRONY_VERSION, CHRONYD_FEATURES);
  516 
  517   DNS_SetAddressFamily(address_family);
  518 
  519   CNF_Initialise(restarted, client_only);
  520 
  521   /* Parse the config file or the remaining command line arguments */
  522   config_args = argc - optind;
  523   if (!config_args) {
  524     CNF_ReadFile(conf_file);
  525   } else {
  526     for (; optind < argc; optind++)
  527       CNF_ParseLine(NULL, config_args + optind - argc + 1, argv[optind]);
  528   }
  529 
  530   /* Check whether another chronyd may already be running */
  531   check_pidfile();
  532 
  533   if (!user)
  534     user = CNF_GetUser();
  535 
  536   pw = getpwnam(user);
  537   if (!pw)
  538     LOG_FATAL("Could not get user/group ID of %s", user);
  539 
  540   /* Create directories for sockets, log files, and dump files */
  541   CNF_CreateDirs(pw->pw_uid, pw->pw_gid);
  542 
  543   /* Write our pidfile to prevent other instances from running */
  544   write_pidfile();
  545 
  546   PRV_Initialise();
  547   LCL_Initialise();
  548   SCH_Initialise();
  549   SYS_Initialise(clock_control);
  550   RTC_Initialise(do_init_rtc);
  551   SRC_Initialise();
  552   RCL_Initialise();
  553   KEY_Initialise();
  554 
  555   /* Open privileged ports before dropping root */
  556   CAM_Initialise(address_family);
  557   NIO_Initialise(address_family);
  558   NCR_Initialise();
  559   CNF_SetupAccessRestrictions();
  560 
  561   /* Command-line switch must have priority */
  562   if (!sched_priority) {
  563     sched_priority = CNF_GetSchedPriority();
  564   }
  565   if (sched_priority) {
  566     SYS_SetScheduler(sched_priority);
  567   }
  568 
  569   if (lock_memory || CNF_GetLockMemory()) {
  570     SYS_LockMemory();
  571   }
  572 
  573   /* Drop root privileges if the specified user has a non-zero UID */
  574   if (!geteuid() && (pw->pw_uid || pw->pw_gid))
  575     SYS_DropRoot(pw->pw_uid, pw->pw_gid);
  576 
  577   REF_Initialise();
  578   SST_Initialise();
  579   NSR_Initialise();
  580   NSD_Initialise();
  581   CLG_Initialise();
  582   MNL_Initialise();
  583   TMC_Initialise();
  584   SMT_Initialise();
  585 
  586   /* From now on, it is safe to do finalisation on exit */
  587   initialised = 1;
  588 
  589   UTI_SetQuitSignalsHandler(signal_cleanup, 1);
  590 
  591   CAM_OpenUnixSocket();
  592 
  593   if (scfilter_level)
  594     SYS_EnableSystemCallFilter(scfilter_level);
  595 
  596   if (ref_mode == REF_ModeNormal && CNF_GetInitSources() > 0) {
  597     ref_mode = REF_ModeInitStepSlew;
  598   }
  599 
  600   REF_SetModeEndHandler(reference_mode_end);
  601   REF_SetMode(ref_mode);
  602 
  603   if (timeout > 0)
  604     SCH_AddTimeoutByDelay(timeout, quit_timeout, NULL);
  605 
  606   if (do_init_rtc) {
  607     RTC_TimeInit(post_init_rtc_hook, NULL);
  608   } else {
  609     post_init_rtc_hook(NULL);
  610   }
  611 
  612   /* The program normally runs under control of the main loop in
  613      the scheduler. */
  614   SCH_MainLoop();
  615 
  616   LOG(LOGS_INFO, "chronyd exiting");
  617 
  618   MAI_CleanupAndExit();
  619 
  620   return 0;
  621 }
  622 
  623 /* ================================================== */