summaryrefslogtreecommitdiff
path: root/agen5/agInit.c
diff options
context:
space:
mode:
Diffstat (limited to 'agen5/agInit.c')
-rw-r--r--agen5/agInit.c684
1 files changed, 684 insertions, 0 deletions
diff --git a/agen5/agInit.c b/agen5/agInit.c
new file mode 100644
index 0000000..ac322bc
--- /dev/null
+++ b/agen5/agInit.c
@@ -0,0 +1,684 @@
+
+/**
+ * @file agInit.c
+ *
+ * Do all the initialization stuff. For daemon mode, only
+ * children will return.
+ *
+ * Time-stamp: "2012-04-14 08:54:02 bkorb"
+ *
+ * This file is part of AutoGen.
+ * Copyright (c) 1992-2012 Bruce Korb - all rights reserved
+ *
+ * AutoGen 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AutoGen 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. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* = = = START-STATIC-FORWARD = = = */
+static char const *
+make_quote_str(char const * str);
+
+static void
+dep_usage(char const * fmt, ...);
+
+static void
+add_sys_env(char * env_name);
+
+static void
+add_env_vars(void);
+/* = = = END-STATIC-FORWARD = = = */
+
+#ifdef DAEMON_ENABLED
+ static bool evalProto(char const ** ppzS, uint16_t* pProto);
+ static void spawnPipe(char const* pzFile);
+ static void spawnListens(char const * pzPort, sa_family_t af);
+ static void daemonize(char const *, char const *, char const *,
+ char const *);
+#endif
+
+#include "expr.ini"
+
+/**
+ * Various initializations.
+ *
+ * @param arg_ct the count of program arguments, plus 1.
+ * @param arg_vec the program name plus its arguments
+ */
+LOCAL void
+initialize(int arg_ct, char** arg_vec)
+{
+ ag_scribble_init();
+
+ /*
+ * Initialize all the Scheme functions.
+ */
+ ag_init();
+ last_scm_cmd = SCHEME_INIT_TEXT;
+ ag_scm_c_eval_string_from_file_line(
+ SCHEME_INIT_TEXT, AG_TEXT_STRTABLE_FILE, SCHEME_INIT_TEXT_LINENO);
+
+ {
+#if GUILE_VERSION > 200000
+ static char const * const module = SCHEME_INIT_DEBUG_2_0;
+#else
+ static char const * const module = SCHEME_INIT_DEBUG_1_6;
+#endif
+ char * p = aprf(INIT_SCM_ERRS_FMT, module);
+ last_scm_cmd = p;
+ ag_scm_c_eval_string_from_file_line(p, __FILE__, __LINE__);
+ AGFREE(p);
+ }
+
+ last_scm_cmd = NULL;
+ processing_state = PROC_STATE_OPTIONS;
+ add_env_vars();
+
+ process_ag_opts(arg_ct, arg_vec);
+ exit_code = AUTOGEN_EXIT_LOAD_ERROR;
+
+ if (OPT_VALUE_TRACE > TRACE_NOTHING)
+ SCM_EVAL_CONST(INIT_SCM_DEBUG_FMT);
+
+#ifdef DAEMON_ENABLED
+
+ if (! HAVE_OPT(DAEMON))
+ return;
+
+#ifdef DEBUG_ENABLED
+ {
+ static char const logf[] = "/tmp/ag-debug.txt";
+ daemonize("/", logf, logf, logf);
+ }
+#else
+ daemonize("/", DEV_NULL, DEV_NULL, DEV_NULL);
+#endif /* DEBUG_ENABLED */
+
+ {
+ sa_family_t af = AF_INET;
+ char const * pzS = OPT_ARG(DAEMON);
+
+ if (evalProto(&pzS, &af))
+ spawnListens(pzS, af);
+ else
+ spawnPipe(pzS);
+ }
+#endif /* DAEMON_ENABLED */
+}
+
+/**
+ * make a name resilient to machinations made by 'make'.
+ * Basically, dollar sign characters are doubled.
+ *
+ * @param str the input string
+ * @returns a newly allocated string with the '$' characters doubled
+ */
+static char const *
+make_quote_str(char const * str)
+{
+ size_t sz = strlen(str) + 1;
+ char const * scan = str;
+ char * res;
+
+ for (;;) {
+ char * p = strchr(scan, '$');
+ if (p == NULL)
+ break;
+ sz++;
+ scan = scan + 1;
+ }
+
+ res = AGALOC(sz, "q name");
+ scan = res;
+
+ for (;;) {
+ char * p = strchr(str, '$');
+
+ if (p == NULL)
+ break;
+ sz = (p - str) + 1;
+ memcpy(res, str, sz);
+ res += sz;
+ str += sz;
+ *(res++) = '$';
+ }
+
+ strcpy(res, str);
+ return scan;
+}
+
+/**
+ * Error in dependency specification
+ *
+ * @param fmt the error message format
+ */
+static void
+dep_usage(char const * fmt, ...)
+{
+ char * msg;
+
+ {
+ va_list ap;
+ va_start(ap, fmt);
+ (void)vasprintf(&msg, fmt, ap);
+ va_end(ap);
+ }
+
+ usage_message(USAGE_INVAL_DEP_OPT_FMT, msg);
+ /* NOTREACHED */
+}
+
+/**
+ * Configure a dependency option.
+ * Handles any of these letters: MFQTPGD as the first part of the option
+ * argument.
+ *
+ * @param opts the autogen options data structure
+ * @param pOptDesc the option descriptor for this option.
+ */
+LOCAL void
+config_dep(tOptions * opts, tOptDesc * od)
+{
+ char const * opt_arg = od->optArg.argString;
+ (void)opts;
+
+ /*
+ * The option argument is optional. Make sure we have one.
+ */
+ if (opt_arg == NULL)
+ return;
+
+ while (*opt_arg == 'M') opt_arg++;
+ opt_arg = SPN_WHITESPACE_CHARS(opt_arg);
+
+ switch (*opt_arg) {
+ case 'F':
+ if (dep_file != NULL)
+ dep_usage(CFGDEP_DUP_TARGET_MSG);
+
+ opt_arg = SPN_WHITESPACE_CHARS(opt_arg + 1);
+ AGDUPSTR(dep_file, opt_arg, "f name");
+ break;
+
+ case 'Q':
+ case 'T':
+ {
+ bool quote_it = (*opt_arg == 'Q');
+
+ if (dep_target != NULL)
+ dep_usage(CFGDEP_DUP_TARGET_MSG);
+
+ opt_arg = SPN_WHITESPACE_CHARS(opt_arg + 1);
+ if (quote_it)
+ dep_target = make_quote_str(opt_arg);
+ else
+ AGDUPSTR(dep_target, opt_arg, "t name");
+ break;
+ }
+
+ case 'P':
+ dep_phonies = true;
+ break;
+
+ case 'D':
+ case 'G':
+ case NUL:
+ /*
+ * 'D' and 'G' make sense to GCC, not us. Ignore 'em. If we
+ * found a NUL byte, then act like we found -MM on the command line.
+ */
+ break;
+
+ default:
+ dep_usage(CFGDEP_UNKNOWN_DEP_FMT, opt_arg);
+ }
+}
+
+/**
+ * Add a system name to the environment. The input name is up-cased and
+ * made to conform to environment variable names. If not already in the
+ * environment, it is added with the string value "1".
+ *
+ * @param env_name in/out: system name to export
+ */
+static void
+add_sys_env(char * env_name)
+{
+ int i = 2;
+
+ for (;;) {
+ if (IS_UPPER_CASE_CHAR(env_name[i]))
+ env_name[i] = tolower(env_name[i]);
+ else if (! IS_ALPHANUMERIC_CHAR(env_name[i]))
+ env_name[i] = '_';
+
+ if (env_name[ ++i ] == NUL)
+ break;
+ }
+
+ /*
+ * If the user already has something in the environment, do not
+ * override it.
+ */
+ if (getenv(env_name) == NULL) {
+ char* pz;
+
+ if (OPT_VALUE_TRACE > TRACE_DEBUG_MESSAGE)
+ fprintf(trace_fp, TRACE_ADD_TO_ENV_FMT, env_name);
+
+ pz = aprf(ADD_SYS_ENV_VAL_FMT, env_name);
+ putenv(pz);
+ }
+}
+
+/**
+ * Define system environment variables. "__autogen__=1" is exported,
+ * along with various ones derivable from Solaris sysinfo(3p) or uname.
+ */
+static void
+add_env_vars(void)
+{
+ /*
+ * Set the last resort search directories first (lowest priority)
+ * The lowest of the low is the config time install data dir.
+ * Next is the *current* directory of this executable.
+ */
+ SET_OPT_TEMPL_DIRS(DFT_TPL_DIR_DATA);
+ SET_OPT_TEMPL_DIRS(DFT_TPL_DIR_RELATIVE);
+
+ {
+ char z[ SCRIBBLE_SIZE ] = "__autogen__";
+#if defined(HAVE_SOLARIS_SYSINFO)
+ static const int nm[] = {
+ SI_SYSNAME, SI_HOSTNAME, SI_ARCHITECTURE, SI_HW_PROVIDER,
+#ifdef SI_PLATFORM
+ SI_PLATFORM,
+#endif
+ SI_MACHINE };
+ int ix;
+ long sz;
+
+ add_sys_env(z);
+ for (ix = 0; ix < sizeof(nm)/sizeof(nm[0]); ix++) {
+ sz = sysinfo(nm[ix], z+2, sizeof(z) - 2);
+ if (sz > 0) {
+ sz += 2;
+ while (z[sz-1] == NUL) sz--;
+ strcpy(z + sz, ADD_ENV_VARS_SUFFIX_FMT+2);
+ add_sys_env(z);
+ }
+ }
+
+#elif defined(HAVE_UNAME_SYSCALL)
+ struct utsname unm;
+
+ add_sys_env(z);
+ if (uname(&unm) != 0)
+ AG_CANT(UNAME_CALL_NAME, SYSCALL_NAME);
+
+ sprintf(z+2, ADD_ENV_VARS_SUFFIX_FMT, unm.sysname);
+ add_sys_env(z);
+
+ sprintf(z+2, ADD_ENV_VARS_SUFFIX_FMT, unm.machine);
+ add_sys_env(z);
+
+ sprintf(z+2, ADD_ENV_VARS_SUFFIX_FMT, unm.nodename);
+ add_sys_env(z);
+#else
+
+ add_sys_env(z);
+#endif
+ }
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * T H E F O L L O W I N G I S D E A D C O D E
+ *
+ * Someday, I want to enable daemon code, but need lotsa time.....
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+#ifdef DAEMON_ENABLED
+
+ static bool
+evalProto(char const ** ppzS, uint16_t* pProto)
+{
+ char const * pzS = *ppzS;
+
+ if (IS_ALPHABETIC_CHAR(*pzS)) {
+ inet_family_map_t* pMap = inet_family_map;
+ do {
+ if (strncmp(pzS, pMap->pz_name, pMap->nm_len) == 0) {
+ *pProto = pMap->family;
+ *ppzS += pMap->nm_len;
+ return true;
+ }
+ } while ((++pMap)->pz_name != NULL);
+ }
+
+ return IS_DEC_DIGIT_CHAR(*pzS);
+}
+
+ LOCAL void
+handleSighup(int sig)
+{
+ redoOptions = true;
+}
+
+ static void
+spawnPipe(char const * pzFile)
+{
+# define S_IRW_ALL \
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH
+ fd_pair_t fdpair;
+ char* pzIn;
+ char* pzOut;
+
+ {
+ size_t len = 2 * (strlen(pzFile) + 5);
+ pzIn = AGALOC(len + 5, "fifo name");
+ pzOut = pzIn + sprintf(pzIn, PIPE_FIFO_IN_NAME_FMT, pzFile) + 1;
+ }
+
+ unlink(pzIn);
+ if ((mkfifo(pzIn, S_IRW_ALL) != 0) && (errno != EEXIST))
+ AG_CANT(PIPE_MKFIFO_NAME, pzIn);
+
+ (void)sprintf(pzOut, PIPE_FIFO_OUT_NAME_FMT, pzFile);
+ unlink(pzOut);
+ if ((mkfifo(pzOut, S_IRW_ALL) != 0) && (errno != EEXIST))
+ AG_CANT(PIPE_MKFIFO_NAME, pzOut);
+
+ fdpair.fd_read = open(pzIn, O_RDONLY);
+ if (fdpair.fd_read < 0)
+ AG_CANT(PIPE_FIFO_OPEN, pzIn);
+
+ {
+ struct pollfd polls[1];
+ polls[0].fd = fdpair.fd_read;
+ polls[0].events = POLLIN | POLLPRI;
+
+ for (;;) {
+ int ct = poll(polls, 1, -1);
+ struct strrecvfd recvfd;
+ pid_t child;
+
+ switch (ct) {
+ case -1:
+ if ((errno != EINTR) || (! redoOptions))
+ goto spawnpipe_finish;
+
+ optionRestore(&autogenOptions);
+ process_ag_opts(autogenOptions.origArgCt,
+ autogenOptions.origArgVect);
+ SET_OPT_DEFINITIONS(PIPE_DEFS_STDIN_STR);
+ break;
+
+ case 1:
+ if ((polls[0].revents & POLLIN) == 0)
+ continue;
+
+ child = fork();
+ switch (child) {
+ default:
+ waitpid(child, &ct, 0);
+ continue;
+
+ case -1:
+ AG_CANT(PIPE_FORK_NAME, zNil);
+
+ case 0:
+ }
+
+ if (dup2(fdpair.fd_read, STDIN_FILENO) != STDIN_FILENO)
+ AG_CANT(PIPE_DUP2_NAME_STR, PIPE_DEFS_STDIN_NAME);
+
+ fdpair.fd_write = open(pzOut, O_WRONLY);
+ if (fdpair.fd_write < 0)
+ AG_CANT(PIPE_FIFO_OPEN, pzOut);
+
+ polls[0].fd = fdpair.fd_write;
+ polls[0].events = POLLOUT;
+ if (poll(polls, 1, -1) != 1)
+ AG_CANT(PIPE_POLL_NAME_STR, PIPE_WRITE_NAME_STR);
+
+ if (dup2(fdpair.fd_write, STDOUT_FILENO) != STDOUT_FILENO)
+ AG_CANT(PIPE_DUP2_NAME_STR, pzOut);
+
+ return;
+ }
+ }
+ }
+
+ spawnpipe_finish:
+ unlink(pzIn);
+ unlink(pzOut);
+ AGFREE(pzIn);
+
+# undef S_IRW_ALL
+
+ exit(EXIT_SUCCESS);
+}
+
+
+ static void
+spawnListens(char const * pzPort, sa_family_t addr_family)
+{
+ int socket_fd = socket(addr_family, SOCK_STREAM, 0);
+ union {
+ struct sockaddr addr;
+ struct sockaddr_in in_addr;
+ struct sockaddr_un un_addr;
+ } sa;
+
+ uint32_t addr_len;
+
+ if (socket_fd < 0)
+ AG_CANT("socket", "AF_INET/SOCK_STREAM");
+
+ switch (addr_family) {
+
+ case AF_UNIX:
+ {
+ uint32_t p_len = strlen(pzPort);
+
+ if (p_len > sizeof(sa.un_addr.sun_path))
+ AG_ABEND(aprf("AF_UNIX path exceeds %d", p_len));
+ sa.un_addr.sun_family = AF_UNIX;
+ strncpy(sa.un_addr.sun_path, pzPort, p_len);
+ addr_len = sizeof(sa.un_addr) - sizeof(sa.un_addr.sun_path) + p_len;
+ }
+ break;
+
+ case AF_INET:
+ {
+ uint16_t port;
+ char* pz;
+
+ sa.in_addr.sin_family = AF_INET;
+ sa.in_addr.sin_addr.s_addr = INADDR_ANY;
+
+ errno = 0;
+ if ((unlink(pzPort) != 0) && (errno != ENOENT))
+ AG_CANT("unlink", pzPort);
+
+ port = (uint16_t)strtol(pzPort, &pz, 0);
+ if ((errno != 0) || (*pz != NUL))
+ AG_ABEND(aprf("Invalid port number: '%s'", pzPort));
+
+ sa.in_addr.sin_port = htons((short)port);
+ addr_len = sizeof(sa.in_addr);
+ }
+ break;
+
+ default:
+ AG_ABEND(aprf("The '%d' address family cannot be handled",
+ addr_family));
+ }
+
+ if (bind(socket_fd, &sa.addr, addr_len) < 0) {
+ char* pz = aprf(LISTEN_PORT_FMT, pzPort, addr_family);
+ AG_CANT("bind", pz);
+ }
+
+ if (fcntl(socket_fd, F_SETFL, O_NONBLOCK) < 0)
+ AG_CANT("socket-fcntl", "FNDELAY");
+
+ if (listen(socket_fd, 5) < 0)
+ AG_CANT("listen", aprf(LISTEN_PORT_FMT, pzPort));
+
+ for (;;) {
+ fd_set fds;
+ int max_fd = socket_fd;
+ int new_conns;
+
+ FD_ZERO(&fds);
+ FD_SET(socket_fd, &fds);
+
+ new_conns = select(max_fd, &fds, NULL, NULL, NULL);
+ if (new_conns < 0) {
+ if (errno == EINTR)
+ continue;
+
+ if (! redoOptions) {
+ unlink(pzPort);
+ exit(EXIT_SUCCESS);
+ }
+
+ optionRestore(&autogenOptions);
+ process_ag_opts(autogenOptions.origArgCt,
+ autogenOptions.origArgVect);
+ SET_OPT_DEFINITIONS("-");
+
+ continue;
+ }
+
+ if (new_conns > 0) {
+ switch (fork()) {
+ default: continue;
+ case -1:
+ AG_CANT("fork", zNil);
+
+ case 0: break;
+ }
+ break;
+ }
+ }
+
+ for (;;) {
+ static int try_ct = 0;
+ struct sockaddr addr;
+ socklen_t addr_len;
+ int fd = accept(socket_fd, &addr, &addr_len);
+ if (fd < 0)
+ switch (errno) {
+ case EINTR:
+ case EAGAIN:
+#if (EAGAIN != EWOULDBLOCK)
+ case EWOULDBLOCK:
+#endif
+ if (try_ct++ < 10000) {
+ sleep(1);
+ continue;
+ }
+ }
+ socket_fd = fd;
+ break;
+ }
+
+ if (dup2(socket_fd, STDOUT_FILENO) != STDOUT_FILENO)
+ AG_CANT("dup2", "out on socket_fd");
+ if (dup2(socket_fd, STDIN_FILENO) != STDIN_FILENO)
+ AG_CANT("dup2", "in on socket_fd");
+}
+
+
+ static void
+daemonize(char const * pzStdin, char const * pzStdout, char const * pzStderr,
+ char const * pzDaemonDir)
+{
+ static char const zNoFork[] = "Error %d while forking: %s\n";
+ /*
+ * Become a daemon process by exiting the current process
+ * and allowing the child to continue. Also, change stdin,
+ * stdout and stderr to point to /dev/null and change to
+ * the root directory ('/').
+ */
+ {
+ int ret = fork();
+
+ switch (ret) {
+ case -1:
+ fprintf(stderr, zNoFork, errno, strerror(errno));
+ default:
+ exit(ret);
+
+ case 0:
+ break;
+ }
+ }
+
+ /*
+ * Now, become a process group and session group leader.
+ */
+ if (setsid() == -1) {
+ fprintf(stderr, "Error %d setting session ID: %s\n",
+ errno, strerror(errno));
+ exit(99);
+ }
+
+ /*
+ * There is now no controlling terminal. However, if we open anything
+ * that resembles a terminal, it will become our controlling terminal.
+ * So, we will fork again and the child will not be a group leader and
+ * thus cannot gain a controlling terminal.
+ */
+ switch (fork()) {
+ case -1:
+ fprintf(stderr, zNoFork, errno, strerror(errno));
+ exit(99);
+
+ case 0:
+ break;
+
+ default:
+ exit(EXIT_SUCCESS); /* parent process - silently go away */
+ }
+
+ umask(0);
+ if (pzDaemonDir == (char*)NULL)
+ pzDaemonDir = "/";
+
+ chdir(pzDaemonDir);
+
+ /*
+ * Reopen the input, output and error files, unless we were told not to
+ */
+ if (pzStdin != (char*)NULL)
+ freopen(pzStdin, "r", stdin);
+
+ if (pzStdout != (char*)NULL)
+ freopen(pzStdout, "w", stdout);
+
+ if (pzStderr != (char*)NULL)
+ freopen(pzStderr, "w", stderr);
+
+ /* We are a daemon now */
+}
+#endif /* DAEMON_ENABLED */
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of agen5/agInit.c */