/* ** SSYNCD: Simple minded filesystem sync daemon ** Copyright (C) 2002 Michael W. Shaffer ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program (see the file COPYING). If not, write to: ** ** The Free Software Foundation, Inc. ** 59 Temple Place, Suite 330, ** Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "conf.h" #include "list.h" #include "hash.h" #include "log.h" #include "ssync.h" char *conf_path_default = "/usr/local/etc/ssyncd.conf"; static void create_pidfile (void) { int fd = -1; char msg[256]; char *path = NULL; path = get_config_string ("pid-path"); if (path) { fd = open (path, O_CREAT|O_EXCL|O_WRONLY|O_TRUNC, 0644); if (fd > -1) { memset (msg, 0, sizeof (msg)); snprintf (msg, (sizeof (msg) - 1), "%d\n", getpid ()); write (fd, msg, strlen (msg)); close (fd); } else { memset (msg, 0, sizeof (msg)); snprintf (msg, (sizeof (msg) - 1), "exiting due to existing pid file: %s", path); LOG (fatal, msg); log_free (); config_free (); exit (0); } } return; } static void remove_pidfile (void) { unlink (get_config_string ("pid-path")); return; } static void main_loop (void) { int i = 0; struct stat s; struct stat d; char *src = NULL; char *dst = NULL; struct datum *da = NULL; struct list_item *curr = NULL; unsigned long interval = 0; unsigned long count = 0; unsigned long stime = 0; unsigned long etime = 0; unsigned long dtime = 0; char bytes[256]; char msg[256]; if ((interval = get_config_long ("interval")) < 1) interval = 30; while (1) { count = 0; LOG (info, "begin run"); memset (&stats, 0, sizeof (struct stats)); hash_table_free (&dups); memset (&dups, 0, sizeof (struct hash_table)); dups.size = 2000; hash_table_init (&dups); stime = time(NULL); for (i = 0 ; i < work.size ; i++) { for (curr = work.tbl[i].head ; curr ; curr = curr->next) { da = (struct datum *) curr->data; if (da) { src = (char *) da->key; dst = (char *) da->val; memset (&s, 0, sizeof (struct stat)); memset (&d, 0, sizeof (struct stat)); lstat (src, &s); lstat (dst, &d); memset (msg, 0, sizeof (msg)); snprintf (msg, (sizeof (msg) - 1), "processing work item: %s --> %s", src, dst); LOG (info, msg); process (src, &s, dst, &d); count++; } } } etime = time (NULL); memset (msg, 0, sizeof (msg)); snprintf (msg, (sizeof (msg) - 1), "end run: %ld work items processed", count); LOG (info, msg); dtime = etime - stime; memset (bytes, 0, sizeof (bytes)); get_bytes (bytes, (sizeof (bytes) - 1)); memset (msg, 0, sizeof (msg)); snprintf (msg, (sizeof (msg) - 1), "statistics: time: %02ld:%02ld:%02ld " "dirs: %ld files: %ld links: %ld " "updated: %ldd/%ldf/%ldl (%s) " "deleted: %ld", (dtime / 3600), ((dtime % 3600) / 60), ((dtime % 3600) % 60), stats.dirs, stats.files, stats.links, stats.dirs_updated, stats.regs_updated, stats.lnks_updated, bytes, stats.deleted); LOG (info, msg); memset (msg, 0, sizeof (msg)); snprintf (msg, (sizeof (msg) - 1), "exceptions: unknowns: %ld errors: %ld", stats.unknowns, stats.errors); LOG (info, msg); memset (msg, 0, sizeof (msg)); snprintf (msg, (sizeof (msg) - 1), "sleeping for %ld seconds...", interval); LOG (info, msg); sleep (interval); } return; } static void signal_handler (int sig) { switch (sig) { case SIGHUP: log_free (); log_init (); break; case SIGINT: case SIGTERM: ssync_shutdown (); remove_pidfile (); log_free (); config_free (); exit (0); break; default: break; } return; } static void daemonize (void) { int i = 0; int pid = 0; int num_fds = 0; char msg[256]; pid = fork (); switch (pid) { case -1: memset (msg, 0, sizeof (msg)); snprintf (msg, (sizeof (msg) - 1), "parent fork failed: %s\n", strerror (errno)); write (2, msg, strlen (msg)); exit (-1); break; case 0: setsid (); break; default: _exit (0); break; } pid = fork (); switch (pid) { case -1: memset (msg, 0, sizeof (msg)); snprintf (msg, (sizeof (msg) - 1), "child fork failed: %s\n", strerror (errno)); write (2, msg, strlen (msg)); exit (-1); break; case 0: if (chdir ("/")) { memset (msg, 0, sizeof (msg)); snprintf (msg, (sizeof (msg) - 1), "chdir '/' failed: %s\n", strerror (errno)); write (2, msg, strlen (msg)); exit (-1); } umask (022); num_fds = (int) sysconf (_SC_OPEN_MAX); for (i = 0 ; i < num_fds ; i++) { close (i); } if (open ("/dev/null", O_RDWR) == -1) exit (-1); if (open ("/dev/null", O_RDWR) == -1) exit (-1); if (open ("/dev/null", O_RDWR) == -1) exit (-1); break; default: _exit (0); break; } return; } int main (int argc, char **argv) { int pri = 0; char *cfg = NULL; int f_no_detach = 0; signal (SIGINT, signal_handler); signal (SIGTERM, signal_handler); signal (SIGHUP, signal_handler); config_load (argc, argv); if ((cfg = get_config_string ("no-detach")) && ((cfg[0] == 'y') || (cfg[0] == 'Y'))) { f_no_detach++; } if (!f_no_detach) { daemonize (); } log_init (); pri = (int) get_config_long ("priority"); if (pri > 20) pri = 20; if (pri < -20) pri = -20; setpriority (PRIO_PROCESS, 0, pri); create_pidfile (); if (ssync_startup (argc, argv) == -1) exit (-1); main_loop (); ssync_shutdown (); remove_pidfile (); log_free (); config_free (); return 0; }