static int create_spawnproc(h2o_configurator_command_t *cmd, yoml_t *node, const char *dirname, char *const *argv, struct sockaddr_un *sa) { int listen_fd, pipe_fds[2] = {-1, -1}; /* build socket path */ sa->sun_family = AF_UNIX; strcpy(sa->sun_path, dirname); strcat(sa->sun_path, "/_"); /* create socket */ if ((listen_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { h2o_configurator_errprintf(cmd, node, "socket(2) failed: %s", strerror(errno)); goto Error; } if (bind(listen_fd, (void *)sa, sizeof(*sa)) != 0) { h2o_configurator_errprintf(cmd, node, "bind(2) failed: %s", strerror(errno)); goto Error; } if (listen(listen_fd, SOMAXCONN) != 0) { h2o_configurator_errprintf(cmd, node, "listen(2) failed: %s", strerror(errno)); goto Error; } /* create pipe which is used to notify the termination of the server */ if (pipe(pipe_fds) != 0) { h2o_configurator_errprintf(cmd, node, "pipe(2) failed: %s", strerror(errno)); pipe_fds[0] = -1; pipe_fds[1] = -1; goto Error; } fcntl(pipe_fds[1], F_SETFD, FD_CLOEXEC); /* spawn */ int mapped_fds[] = {listen_fd, 0, /* listen_fd to 0 */ pipe_fds[0], 5, /* pipe_fds[0] to 5 */ -1}; pid_t pid = h2o_spawnp(argv[0], argv, mapped_fds, 0); if (pid == -1) { fprintf(stderr, "[lib/handler/fastcgi.c] failed to launch helper program %s:%s\n", argv[0], strerror(errno)); goto Error; } close(listen_fd); listen_fd = -1; close(pipe_fds[0]); pipe_fds[0] = -1; return pipe_fds[1]; Error: if (pipe_fds[0] != -1) close(pipe_fds[0]); if (pipe_fds[1]) close(pipe_fds[1]); if (listen_fd != -1) close(listen_fd); unlink(sa->sun_path); return -1; }
static int on_config_hosts(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) { size_t i; if (node->data.mapping.size == 0) { h2o_configurator_errprintf(cmd, node, "the mapping cannot be empty"); return -1; } for (i = 0; i != node->data.mapping.size; ++i) { yoml_t *key = node->data.mapping.elements[i].key; yoml_t *value = node->data.mapping.elements[i].value; h2o_iovec_t hostname; uint16_t port; if (key->type != YOML_TYPE_SCALAR) { h2o_configurator_errprintf(cmd, key, "key (representing the hostname) must be a string"); return -1; } if (h2o_url_parse_hostport(key->data.scalar, strlen(key->data.scalar), &hostname, &port) == NULL) { h2o_configurator_errprintf(cmd, key, "invalid key (must be either `host` or `host:port`)"); return -1; } ctx->hostconf = h2o_config_register_host(ctx->globalconf, hostname, port); if (h2o_configurator_apply_commands(ctx, value, H2O_CONFIGURATOR_FLAG_HOST, NULL) != 0) return -1; if (yoml_get(value, "paths") == NULL) { h2o_configurator_errprintf(NULL, value, "mandatory configuration directive `paths` is missing"); return -1; } ctx->hostconf = NULL; } return 0; }
static int assert_is_extension(h2o_configurator_command_t *cmd, yoml_t *node) { if (node->type != YOML_TYPE_SCALAR) { h2o_configurator_errprintf(cmd, node, "expected a scalar (extension)"); return -1; } if (node->data.scalar[0] != '.') { h2o_configurator_errprintf(cmd, node, "given extension \"%s\" does not start with a \".\"", node->data.scalar); return -1; } return 0; }
static int assert_is_mimetype(h2o_configurator_command_t *cmd, yoml_t *node) { if (node->type != YOML_TYPE_SCALAR) { h2o_configurator_errprintf(cmd, node, "expected a scalar (mime-type)"); return -1; } if (strchr(node->data.scalar, '/') == NULL) { h2o_configurator_errprintf(cmd, node, "the string \"%s\" does not look like a mime-type", node->data.scalar); return -1; } return 0; }
static int on_config_paths(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) { size_t i; /* sort by the length of the path (descending) */ for (i = 0; i != node->data.mapping.size; ++i) { yoml_t *key = node->data.mapping.elements[i].key; if (key->type != YOML_TYPE_SCALAR) { h2o_configurator_errprintf(cmd, key, "key (representing the virtual path) must be a string"); return -1; } } qsort(node->data.mapping.elements, node->data.mapping.size, sizeof(node->data.mapping.elements[0]), (void *)sort_from_longer_paths); for (i = 0; i != node->data.mapping.size; ++i) { yoml_t *key = node->data.mapping.elements[i].key; yoml_t *value = node->data.mapping.elements[i].value; ctx->pathconf = h2o_config_register_path(ctx->hostconf, key->data.scalar); if (h2o_configurator_apply_commands(ctx, value, H2O_CONFIGURATOR_FLAG_PATH, NULL) != 0) return -1; ctx->pathconf = NULL; } return 0; }
ssize_t h2o_configurator_get_one_of(h2o_configurator_command_t *cmd, yoml_t *node, const char *candidates) { const char *config_str, *cand_str; ssize_t config_str_len, cand_index; if (node->type != YOML_TYPE_SCALAR) goto Error; config_str = node->data.scalar; config_str_len = strlen(config_str); cand_str = candidates; for (cand_index = 0;; ++cand_index) { if (strncasecmp(cand_str, config_str, config_str_len) == 0 && (cand_str[config_str_len] == '\0' || cand_str[config_str_len] == ',')) { /* found */ return cand_index; } cand_str = strchr(cand_str, ','); if (cand_str == NULL) goto Error; cand_str += 1; /* skip ',' */ } /* not reached */ Error: h2o_configurator_errprintf(cmd, node, "argument must be one of: %s", candidates); return -1; }
static int on_config_spawn(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) { struct fastcgi_configurator_t *self = (void *)cmd->configurator; char dirname[] = "/tmp/h2o.fcgisock.XXXXXX"; char *argv[] = {h2o_configurator_get_cmd_path("share/h2o/kill-on-close"), "--rm", dirname, "--", "/bin/sh", "-c", node->data.scalar, NULL}; int spawner_fd; struct sockaddr_un sun = {}; h2o_fastcgi_config_vars_t config_vars; int ret = -1; /* create temporary directory */ if (mkdtemp(dirname) == NULL) { h2o_configurator_errprintf(cmd, node, "mkdtemp(3) failed to create temporary directory:%s:%s", dirname, strerror(errno)); dirname[0] = '\0'; goto Exit; } /* launch spawnfcgi command */ if ((spawner_fd = create_spawnproc(cmd, node, dirname, argv, &sun)) == -1) { goto Exit; } config_vars = *self->vars; config_vars.callbacks.dispose = spawnproc_on_dispose; config_vars.callbacks.data = (char *)NULL + spawner_fd; h2o_fastcgi_register_by_address(ctx->pathconf, (void *)&sun, sizeof(sun), &config_vars); ret = 0; Exit: if (dirname[0] != '\0') unlink(dirname); free(argv[0]); return ret; }
static int on_config_mruby_handler_file(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) { struct mruby_configurator_t *self = (void *)cmd->configurator; FILE *fp = NULL; h2o_iovec_t buf = {}; int ret = -1; /* open and read file */ if ((fp = fopen(node->data.scalar, "rt")) == NULL) { h2o_configurator_errprintf(cmd, node, "failed to open file: %s:%s", node->data.scalar, strerror(errno)); goto Exit; } while (!feof(fp)) { buf.base = h2o_mem_realloc(buf.base, buf.len + 65536); buf.len += fread(buf.base + buf.len, 1, 65536, fp); if (ferror(fp)) { h2o_configurator_errprintf(cmd, node, "I/O error occurred while reading file:%s:%s", node->data.scalar, strerror(errno)); goto Exit; } } /* set source */ self->vars->source = buf; buf.base = NULL; self->vars->path = node->data.scalar; /* the value is retained until the end of the configuration phase */ self->vars->lineno = 0; /* check if there is any error in source */ char errbuf[1024]; if (!compile_test(self->vars, errbuf)) { h2o_configurator_errprintf(cmd, node, "failed to compile file:%s:%s", node->data.scalar, errbuf); goto Exit; } /* register */ h2o_mruby_register(ctx->pathconf, self->vars); ret = 0; Exit: if (fp != NULL) fclose(fp); if (buf.base != NULL) free(buf.base); return ret; }
static int on_config(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, const char *file, yoml_t *node) { struct st_h2o_access_log_configurator_t *self = (void*)cmd->configurator; const char *path, *fmt = NULL; h2o_access_log_filehandle_t *fh; switch (node->type) { case YOML_TYPE_SCALAR: path = node->data.scalar; break; case YOML_TYPE_MAPPING: { yoml_t *t; /* get path */ if ((t = yoml_get(node, "path")) == NULL) { h2o_configurator_errprintf(cmd, file, node, "could not find mandatory key `path`"); return -1; } if (t->type != YOML_TYPE_SCALAR) { h2o_configurator_errprintf(cmd, file, t, "`path` must be scalar"); return -1; } path = t->data.scalar; /* get format */ if ((t = yoml_get(node, "format")) != NULL) { if (t->type != YOML_TYPE_SCALAR) { h2o_configurator_errprintf(cmd, file, t, "`format` must be a scalar"); return -1; } fmt = t->data.scalar; } } break; default: h2o_configurator_errprintf(cmd, file, node, "node must be a scalar or a mapping"); return -1; } if ((fh = h2o_access_log_open_handle(path, fmt)) == NULL) return -1; h2o_vector_reserve(NULL, (h2o_vector_t*)self->handles, sizeof(self->handles->entries[0]), self->handles->size + 1); self->handles->entries[self->handles->size++] = fh; return 0; }
static int on_config_header_2arg(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, int cmd_id, yoml_t *node) { h2o_iovec_t *name, value; if (extract_name_value(node->data.scalar, &name, &value) != 0) { h2o_configurator_errprintf(cmd, node, "failed to parse the value; should be in form of `name: value`"); return -1; } if (add_cmd(cmd, node, cmd_id, name, value) != 0) return -1; return 0; }
static int on_config_header_unset(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) { h2o_iovec_t *name; if (extract_name(node->data.scalar, strlen(node->data.scalar), &name) != 0) { h2o_configurator_errprintf(cmd, node, "invalid header name"); return -1; } if (add_cmd(cmd, node, H2O_HEADERS_CMD_UNSET, name, (h2o_iovec_t){}) != 0) return -1; return 0; }
static int on_config(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) { const char *dest; int status = 302; /* default is temporary redirect */ yoml_t *t; switch (node->type) { case YOML_TYPE_SCALAR: dest = node->data.scalar; break; case YOML_TYPE_MAPPING: if ((t = yoml_get(node, "url")) == NULL) { h2o_configurator_errprintf(cmd, node, "mandatory property `url` is missing"); return -1; } if (t->type != YOML_TYPE_SCALAR) { h2o_configurator_errprintf(cmd, t, "property `url` must be a string"); return -1; } dest = t->data.scalar; if ((t = yoml_get(node, "status")) == NULL) { h2o_configurator_errprintf(cmd, node, "mandatory property `status` is missing"); return -1; } if (h2o_configurator_scanf(cmd, t, "%d", &status) != 0) return -1; if (!(300 <= status && status <= 399)) { h2o_configurator_errprintf(cmd, t, "value of property `status` should be within 300 to 399"); return -1; } break; default: h2o_configurator_errprintf(cmd, node, "value must be a string or a mapping"); return -1; } h2o_redirect_register(ctx->pathconf, status, dest); return 0; }
int h2o_configurator_apply(h2o_globalconf_t *config, yoml_t *node) { h2o_configurator_context_t ctx = {config}; if (h2o_configurator_apply_commands(&ctx, node, H2O_CONFIGURATOR_FLAG_GLOBAL, NULL) != 0) return -1; if (config->hosts[0] == NULL) { h2o_configurator_errprintf(NULL, node, "mandatory configuration directive `hosts` is missing"); return -1; } return 0; }
static int add_cmd(h2o_configurator_command_t *cmd, yoml_t *node, int cmd_id, h2o_iovec_t *name, h2o_iovec_t value) { struct headers_configurator_t *self = (void *)cmd->configurator; if (h2o_iovec_is_token(name)) { const h2o_token_t *token = (void *)name; if (h2o_headers_is_prohibited_name(token)) { h2o_configurator_errprintf(cmd, node, "the named header cannot be rewritten"); return -1; } } h2o_vector_reserve(NULL, (h2o_vector_t *)self->cmds, sizeof(self->cmds->entries[0]), self->cmds->size + 1); self->cmds->entries[self->cmds->size++] = (h2o_headers_command_t){cmd_id, name, value}; return 0; }
static int on_config_document_root(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) { struct fastcgi_configurator_t *self = (void *)cmd->configurator; if (node->data.scalar[0] == '\0') { /* unset */ self->vars->document_root = h2o_iovec_init(NULL, 0); } else if (node->data.scalar[0] == '/') { /* set */ self->vars->document_root = h2o_iovec_init(node->data.scalar, strlen(node->data.scalar)); } else { h2o_configurator_errprintf(cmd, node, "value does not start from `/`"); return -1; } return 0; }
int h2o_configurator_scanf(h2o_configurator_command_t *cmd, yoml_t *node, const char *fmt, ...) { va_list args; int sscan_ret; if (node->type != YOML_TYPE_SCALAR) goto Error; va_start(args, fmt); sscan_ret = vsscanf(node->data.scalar, fmt, args); va_end(args); if (sscan_ret != 1) goto Error; return 0; Error: h2o_configurator_errprintf(cmd, node, "argument must match the format: %s", fmt); return -1; }
static int on_config_mruby_handler_path(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) { struct mruby_configurator_t *self = (void *)cmd->configurator; if (node->data.scalar[0] == '\0') { self->vars->mruby_handler_path = h2o_iovec_init(NULL, 0); } else if (node->data.scalar[0] == '/') { self->vars->mruby_handler_path = h2o_iovec_init(node->data.scalar, strlen(node->data.scalar)); h2o_mruby_register(ctx->pathconf, self->vars); } else { h2o_configurator_errprintf(cmd, node, "mruby_handler_path must be absoluted path"); return -1; } return 0; }
static int set_mimetypes(h2o_configurator_command_t *cmd, h2o_mimemap_t *mimemap, yoml_t *node) { size_t i, j; h2o_mimemap_type_t *type = NULL; assert(node->type == YOML_TYPE_MAPPING); for (i = 0; i != node->data.mapping.size; ++i) { yoml_t *key = node->data.mapping.elements[i].key; yoml_t *value = node->data.mapping.elements[i].value; if (assert_is_mimetype(cmd, key) != 0) return -1; type = h2o_mimemap_create_extension_type(key->data.scalar); switch (value->type) { case YOML_TYPE_SCALAR: if (assert_is_extension(cmd, value) != 0) goto Error; h2o_mimemap_set_type(mimemap, value->data.scalar + 1, type, 1); break; case YOML_TYPE_SEQUENCE: for (j = 0; j != value->data.sequence.size; ++j) { yoml_t *ext_node = value->data.sequence.elements[j]; if (assert_is_extension(cmd, ext_node) != 0) goto Error; h2o_mimemap_set_type(mimemap, ext_node->data.scalar + 1, type, 1); } break; default: h2o_configurator_errprintf(cmd, value, "only scalar or sequence of scalar is permitted at the value part of the argument"); goto Error; } h2o_mem_release_shared(type); type = NULL; } return 0; Error: if (type != NULL) h2o_mem_release_shared(type); return -1; }
static int on_config_index(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) { struct st_h2o_file_configurator_t *self = (void *)cmd->configurator; size_t i; free(self->vars->index_files); self->vars->index_files = h2o_mem_alloc(sizeof(self->vars->index_files[0]) * (node->data.sequence.size + 1)); for (i = 0; i != node->data.sequence.size; ++i) { yoml_t *element = node->data.sequence.elements[i]; if (element->type != YOML_TYPE_SCALAR) { h2o_configurator_errprintf(cmd, element, "argument must be a sequence of scalars"); return -1; } self->vars->index_files[i] = element->data.scalar; } self->vars->index_files[i] = NULL; return 0; }
static int on_config_mruby_handler(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) { struct mruby_configurator_t *self = (void *)cmd->configurator; /* set source */ self->vars->source = h2o_strdup(NULL, node->data.scalar, SIZE_MAX); self->vars->path = node->filename; self->vars->lineno = (int)node->line; /* check if there is any error in source */ char errbuf[1024]; if (!compile_test(self->vars, errbuf)) { h2o_configurator_errprintf(cmd, node, "ruby compile error:%s", errbuf); return -1; } /* register */ h2o_mruby_register(ctx->pathconf, self->vars); return 0; }
static int on_config_connect(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) { struct fastcgi_configurator_t *self = (void *)cmd->configurator; const char *hostname = "127.0.0.1", *servname = NULL, *type = "tcp"; /* fetch servname (and hostname) */ switch (node->type) { case YOML_TYPE_SCALAR: servname = node->data.scalar; break; case YOML_TYPE_MAPPING: { yoml_t *t; if ((t = yoml_get(node, "host")) != NULL) { if (t->type != YOML_TYPE_SCALAR) { h2o_configurator_errprintf(cmd, t, "`host` is not a string"); return -1; } hostname = t->data.scalar; } if ((t = yoml_get(node, "port")) == NULL) { h2o_configurator_errprintf(cmd, node, "cannot find mandatory property `port`"); return -1; } if (t->type != YOML_TYPE_SCALAR) { h2o_configurator_errprintf(cmd, node, "`port` is not a string"); return -1; } servname = t->data.scalar; if ((t = yoml_get(node, "type")) != NULL) { if (t->type != YOML_TYPE_SCALAR) { h2o_configurator_errprintf(cmd, t, "`type` is not a string"); return -1; } type = t->data.scalar; } } break; default: h2o_configurator_errprintf(cmd, node, "value must be a string or a mapping (with keys: `port` and optionally `host` and `type`)"); return -1; } if (strcmp(type, "unix") == 0) { /* unix socket */ struct sockaddr_un sa = {}; if (strlen(servname) >= sizeof(sa.sun_path)) { h2o_configurator_errprintf(cmd, node, "path:%s is too long as a unix socket name", servname); return -1; } sa.sun_family = AF_UNIX; strcpy(sa.sun_path, servname); h2o_fastcgi_register_by_address(ctx->pathconf, (void *)&sa, sizeof(sa), self->vars); } else if (strcmp(type, "tcp") == 0) { /* tcp socket */ uint16_t port; if (sscanf(servname, "%" SCNu16, &port) != 1) { h2o_configurator_errprintf(cmd, node, "invalid port number:%s", servname); return -1; } h2o_fastcgi_register_by_hostport(ctx->pathconf, hostname, port, self->vars); } else { h2o_configurator_errprintf(cmd, node, "unknown listen type: %s", type); return -1; } return 0; }
int h2o_configurator_apply_commands(h2o_configurator_context_t *ctx, yoml_t *node, int flags_mask, const char **ignore_commands) { struct { h2o_configurator_command_t *cmd; yoml_t *value; } *deferred; size_t num_deferred = 0, i; if (node->type != YOML_TYPE_MAPPING) { h2o_configurator_errprintf(NULL, node, "node must be a MAPPING"); return -1; } deferred = alloca(sizeof(*deferred) * node->data.mapping.size); /* call on_enter of every configurator */ if (setup_configurators(ctx, 1, node) != 0) return -1; /* handle the configuration commands */ for (i = 0; i != node->data.mapping.size; ++i) { yoml_t *key = node->data.mapping.elements[i].key, *value = node->data.mapping.elements[i].value; h2o_configurator_command_t *cmd; /* obtain the target command */ if (key->type != YOML_TYPE_SCALAR) { h2o_configurator_errprintf(NULL, key, "command must be a string"); return -1; } if (ignore_commands != NULL) { size_t i; for (i = 0; ignore_commands[i] != NULL; ++i) if (strcmp(ignore_commands[i], key->data.scalar) == 0) goto SkipCommand; } if ((cmd = h2o_configurator_get_command(ctx->globalconf, key->data.scalar)) == NULL) { h2o_configurator_errprintf(NULL, key, "unknown command: %s", key->data.scalar); return -1; } if ((cmd->flags & flags_mask) == 0) { h2o_configurator_errprintf(cmd, key, "the command cannot be used at this level"); return -1; } /* check value type */ if ((cmd->flags & (H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR | H2O_CONFIGURATOR_FLAG_EXPECT_SEQUENCE | H2O_CONFIGURATOR_FLAG_EXPECT_MAPPING)) != 0) { switch (value->type) { case YOML_TYPE_SCALAR: if ((cmd->flags & H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR) == 0) { h2o_configurator_errprintf(cmd, value, "argument cannot be a scalar"); return -1; } break; case YOML_TYPE_SEQUENCE: if ((cmd->flags & H2O_CONFIGURATOR_FLAG_EXPECT_SEQUENCE) == 0) { h2o_configurator_errprintf(cmd, value, "argument cannot be a sequence"); return -1; } break; case YOML_TYPE_MAPPING: if ((cmd->flags & H2O_CONFIGURATOR_FLAG_EXPECT_MAPPING) == 0) { h2o_configurator_errprintf(cmd, value, "argument cannot be a mapping"); return -1; } break; default: assert(!"unreachable"); break; } } /* handle the command (or keep it for later execution) */ if ((cmd->flags & H2O_CONFIGURATOR_FLAG_DEFERRED) != 0) { deferred[num_deferred].cmd = cmd; deferred[num_deferred].value = value; ++num_deferred; } else { if (cmd->cb(cmd, ctx, value) != 0) return -1; } SkipCommand: ; } for (i = 0; i != num_deferred; ++i) { if (deferred[i].cmd->cb(deferred[i].cmd, ctx, deferred[i].value) != 0) return -1; } /* call on_enter of every configurator */ if (setup_configurators(ctx, 0, node) != 0) return -1; return 0; }
static int on_config_spawn(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) { struct fastcgi_configurator_t *self = (void *)cmd->configurator; char *spawn_user = NULL, *spawn_cmd; char dirname[] = "/tmp/h2o.fcgisock.XXXXXX"; char *argv[10]; int spawner_fd; struct sockaddr_un sa = {}; h2o_fastcgi_config_vars_t config_vars; int ret = -1; switch (node->type) { case YOML_TYPE_SCALAR: spawn_user = ctx->globalconf->user; spawn_cmd = node->data.scalar; break; case YOML_TYPE_MAPPING: { yoml_t *t; if ((t = yoml_get(node, "command")) == NULL) { h2o_configurator_errprintf(cmd, node, "mandatory attribute `command` does not exist"); return -1; } if (t->type != YOML_TYPE_SCALAR) { h2o_configurator_errprintf(cmd, node, "attribute `command` must be scalar"); return -1; } spawn_cmd = t->data.scalar; if ((t = yoml_get(node, "user")) != NULL) { if (t->type != YOML_TYPE_SCALAR) { h2o_configurator_errprintf(cmd, node, "attribute `user` must be scalar"); return -1; } spawn_user = t->data.scalar; } } break; default: h2o_configurator_errprintf(cmd, node, "argument must be scalar or mapping"); return -1; } { /* build args */ size_t i = 0; argv[i++] = h2o_configurator_get_cmd_path("share/h2o/kill-on-close"); argv[i++] = "--rm"; argv[i++] = dirname; argv[i++] = "--"; if (spawn_user != NULL) { argv[i++] = h2o_configurator_get_cmd_path("share/h2o/setuidgid"); argv[i++] = spawn_user; } argv[i++] = "/bin/sh"; argv[i++] = "-c"; argv[i++] = spawn_cmd; argv[i++] = NULL; assert(i <= sizeof(argv) / sizeof(argv[0])); } if (ctx->dry_run) { dirname[0] = '\0'; spawner_fd = -1; sa.sun_family = AF_UNIX; strcpy(sa.sun_path, "/dry-run.nonexistent"); } else { /* create temporary directory */ if (mkdtemp(dirname) == NULL) { h2o_configurator_errprintf(cmd, node, "mkdtemp(3) failed to create temporary directory:%s:%s", dirname, strerror(errno)); dirname[0] = '\0'; goto Exit; } /* launch spawnfcgi command */ if ((spawner_fd = create_spawnproc(cmd, node, dirname, argv, &sa)) == -1) { goto Exit; } } config_vars = *self->vars; config_vars.callbacks.dispose = spawnproc_on_dispose; config_vars.callbacks.data = (char *)NULL + spawner_fd; h2o_fastcgi_register_by_address(ctx->pathconf, (void *)&sa, sizeof(sa), &config_vars); ret = 0; Exit: if (dirname[0] != '\0') unlink(dirname); free(argv[0]); return ret; }
static int on_config_spawn(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) { struct fastcgi_configurator_t *self = (void *)cmd->configurator; char *spawn_user = NULL, *spawn_cmd; char *kill_on_close_cmd_path = NULL, *setuidgid_cmd_path = NULL; char dirname[] = "/tmp/h2o.fcgisock.XXXXXX"; char *argv[10]; int spawner_fd; struct sockaddr_un sa; h2o_fastcgi_config_vars_t config_vars; int ret = -1; struct passwd h2o_user_pwbuf, *h2o_user_pw; char h2o_user_buf[65536]; memset(&sa, 0, sizeof(sa)); switch (node->type) { case YOML_TYPE_SCALAR: spawn_user = ctx->globalconf->user; spawn_cmd = node->data.scalar; break; case YOML_TYPE_MAPPING: { yoml_t *t; if ((t = yoml_get(node, "command")) == NULL) { h2o_configurator_errprintf(cmd, node, "mandatory attribute `command` does not exist"); return -1; } if (t->type != YOML_TYPE_SCALAR) { h2o_configurator_errprintf(cmd, node, "attribute `command` must be scalar"); return -1; } spawn_cmd = t->data.scalar; spawn_user = ctx->globalconf->user; if ((t = yoml_get(node, "user")) != NULL) { if (t->type != YOML_TYPE_SCALAR) { h2o_configurator_errprintf(cmd, node, "attribute `user` must be scalar"); return -1; } spawn_user = t->data.scalar; } } break; default: h2o_configurator_errprintf(cmd, node, "argument must be scalar or mapping"); return -1; } /* obtain uid & gid of the client that connects to the FastCGI daemon (i.e. H2O after dropping privileges) */ if (ctx->globalconf->user != NULL) { /* change ownership of temporary directory */ if (getpwnam_r(ctx->globalconf->user, &h2o_user_pwbuf, h2o_user_buf, sizeof(h2o_user_buf), &h2o_user_pw) != 0 || h2o_user_pw == NULL) { h2o_configurator_errprintf(cmd, node, "getpwnam_r(3) failed to obtain uid of user:%s", ctx->globalconf->user); goto Exit; } } else { h2o_user_pw = NULL; } { /* build args */ size_t i = 0; argv[i++] = kill_on_close_cmd_path = h2o_configurator_get_cmd_path("share/h2o/kill-on-close"); argv[i++] = "--rm"; argv[i++] = dirname; argv[i++] = "--"; if (spawn_user != NULL) { argv[i++] = setuidgid_cmd_path = h2o_configurator_get_cmd_path("share/h2o/setuidgid"); argv[i++] = spawn_user; } argv[i++] = "/bin/sh"; argv[i++] = "-c"; argv[i++] = spawn_cmd; argv[i++] = NULL; assert(i <= sizeof(argv) / sizeof(argv[0])); } if (ctx->dry_run) { dirname[0] = '\0'; spawner_fd = -1; sa.sun_family = AF_UNIX; strcpy(sa.sun_path, "/dry-run.nonexistent"); } else { /* create temporary directory */ if (mkdtemp(dirname) == NULL) { h2o_configurator_errprintf(cmd, node, "mkdtemp(3) failed to create temporary directory:%s:%s", dirname, strerror(errno)); dirname[0] = '\0'; goto Exit; } /* change ownership of temporary directory */ if (h2o_user_pw != NULL && chown(dirname, h2o_user_pw->pw_uid, h2o_user_pw->pw_gid) != 0) { h2o_configurator_errprintf(cmd, node, "chown(2) failed to change ownership of temporary directory:%s:%s", dirname, strerror(errno)); goto Exit; } /* launch spawnfcgi command */ if ((spawner_fd = create_spawnproc(cmd, node, dirname, argv, &sa, h2o_user_pw)) == -1) { goto Exit; } } config_vars = *self->vars; config_vars.callbacks.dispose = spawnproc_on_dispose; config_vars.callbacks.data = (char *)NULL + spawner_fd; h2o_fastcgi_register_by_address(ctx->pathconf, (void *)&sa, sizeof(sa), &config_vars); ret = 0; Exit: if (dirname[0] != '\0') unlink(dirname); free(kill_on_close_cmd_path); free(setuidgid_cmd_path); return ret; }
int h2o_configurator_apply_commands(h2o_configurator_context_t *ctx, yoml_t *node, int flags_mask, const char **ignore_commands) { struct st_cmd_value_t { h2o_configurator_command_t *cmd; yoml_t *value; }; H2O_VECTOR(struct st_cmd_value_t) deferred = {}, semi_deferred = {}; size_t i; int ret = -1; if (node->type != YOML_TYPE_MAPPING) { h2o_configurator_errprintf(NULL, node, "node must be a MAPPING"); goto Exit; } /* call on_enter of every configurator */ if (setup_configurators(ctx, 1, node) != 0) goto Exit; /* handle the configuration commands */ for (i = 0; i != node->data.mapping.size; ++i) { yoml_t *key = node->data.mapping.elements[i].key, *value = node->data.mapping.elements[i].value; h2o_configurator_command_t *cmd; /* obtain the target command */ if (key->type != YOML_TYPE_SCALAR) { h2o_configurator_errprintf(NULL, key, "command must be a string"); goto Exit; } if (ignore_commands != NULL) { size_t i; for (i = 0; ignore_commands[i] != NULL; ++i) if (strcmp(ignore_commands[i], key->data.scalar) == 0) goto SkipCommand; } if ((cmd = h2o_configurator_get_command(ctx->globalconf, key->data.scalar)) == NULL) { h2o_configurator_errprintf(NULL, key, "unknown command: %s", key->data.scalar); goto Exit; } if ((cmd->flags & flags_mask) == 0) { h2o_configurator_errprintf(cmd, key, "the command cannot be used at this level"); goto Exit; } /* check value type */ if ((cmd->flags & (H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR | H2O_CONFIGURATOR_FLAG_EXPECT_SEQUENCE | H2O_CONFIGURATOR_FLAG_EXPECT_MAPPING)) != 0) { switch (value->type) { case YOML_TYPE_SCALAR: if ((cmd->flags & H2O_CONFIGURATOR_FLAG_EXPECT_SCALAR) == 0) { h2o_configurator_errprintf(cmd, value, "argument cannot be a scalar"); goto Exit; } break; case YOML_TYPE_SEQUENCE: if ((cmd->flags & H2O_CONFIGURATOR_FLAG_EXPECT_SEQUENCE) == 0) { h2o_configurator_errprintf(cmd, value, "argument cannot be a sequence"); goto Exit; } break; case YOML_TYPE_MAPPING: if ((cmd->flags & H2O_CONFIGURATOR_FLAG_EXPECT_MAPPING) == 0) { h2o_configurator_errprintf(cmd, value, "argument cannot be a mapping"); goto Exit; } break; default: assert(!"unreachable"); break; } } /* handle the command (or keep it for later execution) */ if ((cmd->flags & H2O_CONFIGURATOR_FLAG_SEMI_DEFERRED) != 0) { h2o_vector_push_back(NULL, &semi_deferred, ((struct st_cmd_value_t){cmd, value})); } else if ((cmd->flags & H2O_CONFIGURATOR_FLAG_DEFERRED) != 0) {
static int on_config_custom_handler(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) { static const char *ignore_commands[] = {"extension", NULL}; struct st_h2o_file_configurator_t *self = (void *)cmd->configurator; h2o_pathconf_t *prev_pathconf = ctx->pathconf; yoml_t *ext_node; h2o_mimemap_type_t *type = NULL; int ret = -1; if (node->type != YOML_TYPE_MAPPING) { h2o_configurator_errprintf(cmd, node, "argument must be a MAPPING"); goto Exit; } if ((ext_node = yoml_get(node, "extension")) == NULL) { h2o_configurator_errprintf(cmd, node, "mandatory key `extension` is missing"); goto Exit; } type = h2o_mimemap_create_dynamic_type(ctx->globalconf); ctx->pathconf = &type->data.dynamic.pathconf; if (h2o_configurator_apply_commands(ctx, node, H2O_CONFIGURATOR_FLAG_EXTENSION, ignore_commands) != 0) goto Exit; switch (type->data.dynamic.pathconf.handlers.size) { case 1: break; case 0: h2o_configurator_errprintf(cmd, node, "no handler declared for given extension"); goto Exit; default: h2o_configurator_errprintf(cmd, node, "cannot assign more than one handler for given extension"); goto Exit; } clone_mimemap_if_clean(self); switch (ext_node->type) { case YOML_TYPE_SCALAR: if (assert_is_extension(cmd, ext_node) != 0) goto Exit; h2o_mimemap_set_type(self->vars->mimemap, ext_node->data.scalar + 1, type, 1); break; case YOML_TYPE_SEQUENCE: { size_t i; for (i = 0; i != ext_node->data.sequence.size; ++i) { yoml_t *n = ext_node->data.sequence.elements[i]; if (assert_is_extension(cmd, n) != 0) goto Exit; h2o_mimemap_set_type(self->vars->mimemap, n->data.scalar + 1, type, 1); } } break; default: h2o_configurator_errprintf(cmd, ext_node, "only scalar or sequence of scalar is permitted at the value part of the argument"); goto Exit; } ret = 0; Exit: if (type != NULL) h2o_mem_release_shared(type); ctx->pathconf = prev_pathconf; return ret; }
static int on_config_mruby_handler_path(h2o_configurator_command_t *cmd, h2o_configurator_context_t *ctx, yoml_t *node) { h2o_configurator_errprintf(cmd, node, "the command has been removed; see https://github.com/h2o/h2o/pull/467"); return -1; }