/* xephemdbd: read lines of FOV requests and produce object lists in .edb or * plain text format. Requests which specify their own output file are each * handled by a separate child process. */ #include #include #include #include #include #include #include #include #include #include #include #include "xephem.h" extern int handle_request (char *req, int nofs); extern void db_read (char *fn); extern int fs_setup (char catdir[], char msg[]); static void usage (void); static void chklkfile (void); static void mklkfile (void); static void newstdin (void); static void daemonize (void); static void find_edb (void); static void go(void); static void new_process (char *req); static void rmlkfile(void); static char catdir_def[] = "/usr/local/xephem/catalogs"; static char moddir_def[] = "/usr/local/xephem/auxil"; static char lkfile_def[] = "/tmp/xephemdbd.pid"; static int dflag; static int vflag; static int fflag; static char *pname; static char *catdir = catdir_def; static char *moddir = moddir_def; static char *lkfile = lkfile_def; static char *ifile; /* we exit after this many minutes with no request. * but we allow waiting forever for that first exciting one. */ #define IDLET_DEF 30 static int idlet = IDLET_DEF; int main (int ac, char *av[]) { /* save name */ pname = av[0]; /* scan args */ while ((--ac > 0) && ((*++av)[0] == '-')) { char *s; for (s = av[0]+1; *s != '\0'; s++) switch (*s) { case 'c': if (ac < 2) usage(); catdir = *++av; ac--; break; case 'd': dflag++; break; case 'f': fflag++; break; case 'i': if (ac < 2) usage (); ifile = *++av; ac--; break; case 'l': if (ac < 2) usage (); lkfile = *++av; ac--; break; case 'm': if (ac < 2) usage (); moddir = *++av; ac--; break; case 't': if (ac < 2) usage (); idlet = atoi(*++av); ac--; break; case 'v': vflag++; break; default: usage(); break; } } if (ac > 0) usage(); /* see if already running */ chklkfile (); /* fork/exit unless d flag */ if (!dflag) daemonize(); /* make the lock file */ mklkfile (); /* read in all edb files and check for ppm and gsc */ find_edb(); /* reassign stdin if given -i */ if (ifile) newstdin (); /* start listening for commands on stdin until eof */ go(); rmlkfile(); return (0); } static void usage() { FILE *fp = stderr; fprintf(fp,"%s [options]:\n", pname); fprintf(fp,"Options:\n"); fprintf(fp," -c alternate catalogs directory;\n"); fprintf(fp," default is %s\n", catdir_def); fprintf(fp," -d do not fork/exit as a daemon process\n"); fprintf(fp," -f do not try to use field star catalogs\n"); fprintf(fp," -i open file R/W for input; default is stdin\n"); fprintf(fp," -l lock file; default is %s\n",lkfile_def); fprintf(fp," -m dir of moon models; default is %s\n",moddir_def); fprintf(fp," -t max idle minutes, 0=forever. default is %d\n", IDLET_DEF); fprintf(fp," -v verbose to stderr\n"); fprintf(fp,"\n"); fprintf(fp,"Input format:\n"); fprintf(fp," [>file] optional input source, else stdin\n"); fprintf(fp," outputmode 1:column; 2:topo; 4:apparent; 8:header\n"); fprintf(fp," objtypes 15:sol sys; 16:br stars; 224:deep sky; 768:field\n"); fprintf(fp," year time of ephemerides, decimal year\n"); fprintf(fp," RA,Dec position of center, rads\n"); fprintf(fp," FOV field of view, rads\n"); fprintf(fp," Mag limiting magnitude\n"); fprintf(fp," [lt,lg,el] location, if topocentric\n"); exit (1); } /* if lkfile indicates another xephemdbd is running exit 0; * if we can't tell but can't rule it out exit 1; * else just return, which means we appear to be alone and it's ok to run. */ static void chklkfile () { char buf[1024]; int n, fd; fd = open (lkfile, O_RDONLY); if (fd < 0) { if (errno == EACCES) { fprintf (stderr, "%s: exists but can not read to check pid\n", lkfile); exit (1); } } else { n = read (fd, buf, sizeof(buf)); close (fd); if (n < 0) { fprintf (stderr, "%s: %s\n", lkfile, strerror(errno)); exit (1); } buf[n] = '\0'; n = atoi (buf); if (kill (n, 0) == 0 || errno != ESRCH) { if (vflag) fprintf (stderr, "Already running\n"); exit(0); } } } /* make the lock file with our pid in it. * exit if trouble. */ static void mklkfile () { char buf[1024]; int n, fd; fd = open (lkfile, O_CREAT|O_WRONLY, 0644); if (fd < 0) { fprintf (stderr, "%s: %s\n", lkfile, strerror(errno)); exit (1); } n = sprintf (buf, "%d\n", getpid()); if (write (fd, buf, n) < 0) { fprintf (stderr, "%s: %s\n", lkfile, strerror(errno)); exit (1); } close (fd); } static void rmlkfile() { remove (lkfile); } /* reopen stdin as ifile. * N.B. open R/W in case it is a fifo so we will never see EOF and can * stand by forever waiting for requests. */ static void newstdin () { if (!freopen (ifile, "r+", stdin)) { fprintf (stderr, "%s: %s\n", ifile, strerror(errno)); rmlkfile(); exit (1); } } /* make a new process to serve as the daemon. * this only returns if we are the new daemon process. */ static void daemonize() { int i; long n; switch (fork()) { case -1: perror ("fork"); exit (1); break; case 0: /* close all, but preserve out/err for messages */ n = sysconf (_SC_OPEN_MAX); for (i = 3; i < n; i++) (void) close (i); (void) setsid(); break; default: exit (0); } } /* read in all edb we can find. * also check for ppm and gsc files. */ static void find_edb() { struct dirent *dirent; char buf[1024]; DIR *dir; /* register moon model dir */ setMoonDir (moddir); /* check for field star catalogs unless disabled */ if (!fflag) { if (vflag) fprintf (stderr, "Checking GSC and PPM\n"); if (fs_setup(catdir, buf) < 0) { fprintf (stderr, "%s\n", buf); rmlkfile(); exit (1); } } /* open dir */ dir = opendir (catdir); if (!dir) { fprintf (stderr, "%s: %s", catdir, strerror(errno)); rmlkfile(); exit (1); } /* scan for and read each .edb catalog -- skip sao! */ if (vflag) fprintf (stderr, "Loading %s/*.edb (except sao.edb)\n", catdir); while ((dirent = readdir (dir)) != NULL) { char *name = dirent->d_name; int l = strlen (name); if (l > 4 && !strcasecmp (name+(l-4), ".edb") && strncasecmp (name, "sao", 3)) { (void) sprintf (buf, "%s/%s", catdir, name); if (vflag) fprintf (stderr, " %s\n", name); db_read (buf); } } if (vflag) fprintf (stderr, "done\n"); (void) closedir (dir); } static void on_alarm (int signo) { if (vflag) fprintf (stderr, "Idle time-out\n"); rmlkfile(); exit (0); } /* loop reading stdin until see eof, sending results to stdout. * if nothing after idlet minutes, exit (but allow first read to wait forever). */ static void go() { char request[1024]; /* no zombies */ signal (SIGCHLD, SIG_IGN); /* prepare for SIGALRM */ signal (SIGALRM, on_alarm); if (vflag) fprintf (stderr, "%6d: Master daemon pid\n", getpid()); while (fgets (request, sizeof(request), stdin) != NULL) { alarm (0); /* cancel timeout */ request[strlen(request)-1] = '\0'; /* strip \n */ if (vflag) fprintf (stderr, "Request: %s\n", request); if (request[0] == '>') new_process (request); else (void) handle_request (request, fflag); alarm (60*idlet); /* arm timeout */ } } /* handle the given request in its own process. */ static void new_process (char *req) { int pid; pid = fork(); if (pid == 0) { /* child */ char *fnp; /* after '>' and up to ',' is new file name */ fnp = strchr (req++, ','); if (!fnp) { fprintf (stderr, "No file name\n"); exit(1); } *fnp = '\0'; /* open req file as stdout, no buffering */ if (vflag) fprintf (stderr, "Opening %s\n", req); if (!freopen (req, "w", stdout)) { fprintf (stderr, "%s: %s", req, strerror(errno)); exit(1); } setbuf (stdout, NULL); /* handle remainder of string as the request as usual */ (void) handle_request (fnp + 1, fflag); exit(0); } else if (pid < 0) { perror ("fork"); rmlkfile(); exit (1); } /* parent just resumes */ }