#include "config.h" #include #include #include #include #include #include #include #include "alternative.h" #include "libstr.h" #include "liblist.h" #include "userconfig.h" #define MAX_CONFIG_LINE KILOBYTE #define DELIMITER ';' /* Type definitions */ typedef struct type_php_fcgi { char *executable; char *chroot; char *binding; uid_t uid; gid_t gid; char *config_file; t_groups groups; struct type_php_fcgi *next; } t_php_fcgi; typedef struct { char *pidfile; char *forks; t_php_fcgi *php_fcgi; t_keyvalue *envir; } t_config; /* Code */ int run_php_fcgi(t_php_fcgi *php_fcgi) { pid_t pid; if ((pid = fork()) == 0) { if (php_fcgi->chroot != NULL) { if (chdir(php_fcgi->chroot) == -1) { exit(EXIT_FAILURE); } else if (chroot(php_fcgi->chroot) == -1) { exit(EXIT_FAILURE); } } do { if (setgroups(php_fcgi->groups.number, php_fcgi->groups.array) != -1) { if (setgid(php_fcgi->gid) != -1) { if (setuid(php_fcgi->uid) != -1) { break; } } } fprintf(stderr, "\nError while changing uid/gid!\n"); exit(EXIT_FAILURE); } while (false); if (php_fcgi->config_file == NULL) { execlp(php_fcgi->executable, php_fcgi->executable, "-b", php_fcgi->binding, (char*)NULL); } else { execlp(php_fcgi->executable, php_fcgi->executable, "-b", php_fcgi->binding, "-c", php_fcgi->config_file, (char*)NULL); } fprintf(stderr, "Can't execute %s\n", php_fcgi->executable); exit(EXIT_FAILURE); } return pid; } int parse_line(t_config *config, char *line) { char *key, *value, *item, *group, *pipe; t_php_fcgi *new; if (split_configline(line, &key, &value) != -1) { strlower(key); if (strcmp(key, "pidfile") == 0) { free(config->pidfile); if ((config->pidfile = strdup(value)) == NULL) { return -1; } } else if (strcmp(key, "forks") == 0) { free(config->forks); if (str2int(value) < 1) { return -1; } if ((config->forks = strdup(value)) == NULL) { return -1; } } else if (strcmp(key, "setenv") == 0) { if (parse_keyvaluelist(value, &(config->envir), "=") != -1) { return true; } } else if (strcmp(key, "server") == 0) { if ((new = (t_php_fcgi*)malloc(sizeof(t_php_fcgi))) == NULL) { return -1; } new->next = config->php_fcgi; config->php_fcgi = new; /* PHP executable */ if (split_string(value, &item, &value, DELIMITER) == -1) { return -1; } if ((pipe = strchr(item, '|')) != NULL) { *pipe = '\0'; if ((new->chroot = strdup(item)) == NULL) { return -1; } *pipe = '/'; item = pipe; } else { new->chroot = NULL; } if ((new->executable = strdup(item)) == NULL) { return -1; } /* Binding */ if (split_string(value, &item, &value, DELIMITER) == -1) { return -1; } if ((new->binding = strdup(item)) == NULL) { return -1; } /* UID, GID's */ split_string(value, &item, &value, DELIMITER); split_string(item, &item, &group, ':'); if (parse_userid(item, &(new->uid)) != 1) { return -1; } if (group != NULL) { if (parse_groups(group, &(new->gid), &(new->groups)) != 1) { return -1; } } else { if (lookup_group_ids(new->uid, &(new->gid), &(new->groups)) != 1) { return -1; } } /* Configuration file */ if (value != NULL) { if ((new->config_file = strdup(value)) == NULL) { return -1; } } else { new->config_file = NULL; } } else { return -1; } } return 0; } t_config *read_config(char *config_file) { t_config *config; FILE *fp; char line[MAX_CONFIG_LINE + 1], *data; int linenr = 0; if ((config = (t_config*)malloc(sizeof(t_config))) == NULL) { return NULL; } /* Default settings */ config->pidfile = strdup(PIDFILE_DIR"/php-fcgi.pid"); config->forks = strdup("3"); config->php_fcgi = NULL; config->envir = NULL; if ((fp = fopen(config_file, "r")) == NULL) { perror(config_file); return NULL; } line[MAX_CONFIG_LINE] = '\0'; while (fgets(line, MAX_CONFIG_LINE, fp) != NULL) { linenr++; data = strip_line(line); if (*data != '\0') { if (parse_line(config, data) == -1) { fprintf(stderr, "Syntax error in %s on line %d\n", config_file, linenr); } } } fclose(fp); return config; } void show_help(char *php_fcgi) { printf("Usage: %s [options]\n", php_fcgi); printf("Options: -c : the configuration file to be used.\n"); printf(" -h: show this information and exit.\n"); printf(" -k: kill running FastCGI servers.\n"); printf(" -v: show version and exit.\n"); } int main(int argc, char *argv[]) { t_config *config; t_keyvalue *env; char *config_file = CONFIG_DIR"/php-fcgi.conf"; FILE *fp; pid_t pid; bool kill_servers = false; char line[11]; size_t len; int i = 0; while (++i < argc) { if (strcmp(argv[i], "-c") == 0) { if (++i < argc) { config_file = argv[i]; } else { fprintf(stderr, "Specify a configuration file.\n"); return EXIT_FAILURE; } } else if (strcmp(argv[i], "-k") == 0) { kill_servers = true; } else if (strcmp(argv[i], "-h") == 0) { show_help(argv[0]); return EXIT_SUCCESS; } else if (strcmp(argv[i], "-v") == 0) { printf("PHP-FastCGI v"VERSION"\n"); return EXIT_SUCCESS; } else { fprintf(stderr, "Unknown option. Use '-h' for help.\n"); return EXIT_FAILURE; } } if ((config = read_config(config_file)) == NULL) { return EXIT_FAILURE; } if (kill_servers == false) { /* Start PHP FastCGI servers */ clearenv(); setenv("PHP_FCGI_CHILDREN", config->forks, 1); setenv("PHP_FCGI_MAX_REQUESTS", "500", 1); env = config->envir; while (env != NULL) { setenv(env->key, env->value, 1); env = env->next; } if (config->php_fcgi != NULL) { if ((fp = fopen(config->pidfile, "r")) != NULL) { fclose(fp); fprintf(stderr, "A PID-file already exists. Are FastCGI daemons already running?\n"); return EXIT_FAILURE; } if ((fp = fopen(config->pidfile, "a")) != NULL) { while (config->php_fcgi != NULL) { if ((pid = run_php_fcgi(config->php_fcgi)) != -1) { fprintf(fp, "%d\n", (int)pid); } else { fprintf(stderr, "Can't start FastCGI server\n"); } config->php_fcgi = config->php_fcgi->next; } fclose(fp); } else { fprintf(stderr, "write "); perror(config->pidfile); } } } else { /* Kill PHP FastCGI servers */ if ((fp = fopen(config->pidfile, "r")) != NULL) { line[10] = '\0'; while (fgets(line, 10, fp) != NULL) { if ((len = strlen(line)) > 0) { if (line[len - 1] == '\n') { line[len - 1] = '\0'; } if ((pid = (pid_t)str2int(line)) > 1) { if (kill(pid, 15) == -1) { fprintf(stderr, "kill -15 "); perror(line); } } } } fclose(fp); if (unlink(config->pidfile) == -1) { fprintf(stderr, "unlink "); perror(config->pidfile); } } else { fprintf(stderr, "read "); perror(config->pidfile); } } return EXIT_SUCCESS; }