📄 mod_cgid.c
字号:
req.ugid = empty_ugid; } else { memcpy(&req.ugid, ugid, sizeof(ap_unix_identity_t)); } req.req_type = req_type; req.ppid = parent_pid; req.conn_id = r->connection->id; req.core_module_index = core_module.module_index; for (req.env_count = 0; env[req.env_count]; req.env_count++) { continue; } req.filename_len = strlen(r->filename); req.argv0_len = strlen(argv0); req.uri_len = strlen(r->uri); req.args_len = r->args ? strlen(r->args) : 0; req.loglevel = r->server->loglevel; /* Write the request header */ if ((stat = sock_write(fd, &req, sizeof(req))) != APR_SUCCESS) { return stat; } /* Write filename, argv0, uri, and args */ if ((stat = sock_write(fd, r->filename, req.filename_len)) != APR_SUCCESS || (stat = sock_write(fd, argv0, req.argv0_len)) != APR_SUCCESS || (stat = sock_write(fd, r->uri, req.uri_len)) != APR_SUCCESS) { return stat; } if (req.args_len) { if ((stat = sock_write(fd, r->args, req.args_len)) != APR_SUCCESS) { return stat; } } /* write the environment variables */ for (i = 0; i < req.env_count; i++) { apr_size_t curlen = strlen(env[i]); if ((stat = sock_write(fd, &curlen, sizeof(curlen))) != APR_SUCCESS) { return stat; } if ((stat = sock_write(fd, env[i], curlen)) != APR_SUCCESS) { return stat; } }#if 0#ifdef RLIMIT_CPU if (conf->limit_cpu) { len = 1; stat = sock_write(fd, &len, sizeof(int)); stat = sock_write(fd, conf->limit_cpu, sizeof(struct rlimit)); } else { len = 0; stat = sock_write(fd, &len, sizeof(int)); }#endif#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) if (conf->limit_mem) { len = 1; stat = sock_write(fd, &len, sizeof(int)); stat = sock_write(fd, conf->limit_mem, sizeof(struct rlimit)); } else { len = 0; stat = sock_write(fd, &len, sizeof(int)); }#endif#ifdef RLIMIT_NPROC if (conf->limit_nproc) { len = 1; stat = sock_write(fd, &len, sizeof(int)); stat = sock_write(fd, conf->limit_nproc, sizeof(struct rlimit)); } else { len = 0; stat = sock_write(fd, &len, sizeof(int)); }#endif#endif return APR_SUCCESS;}static void daemon_signal_handler(int sig){ if (sig == SIGHUP) { ++daemon_should_exit; }}static void cgid_child_errfn(apr_pool_t *pool, apr_status_t err, const char *description){ request_rec *r; void *vr; apr_pool_userdata_get(&vr, ERRFN_USERDATA_KEY, pool); r = vr; /* sure we got r, but don't call ap_log_rerror() because we don't * have r->headers_in and possibly other storage referenced by * ap_log_rerror() */ ap_log_error(APLOG_MARK, APLOG_ERR, err, r->server, "%s", description);}static int cgid_server(void *data){ struct sockaddr_un unix_addr; int sd, sd2, rc; mode_t omask; apr_socklen_t len; apr_pool_t *ptrans; server_rec *main_server = data; apr_hash_t *script_hash = apr_hash_make(pcgi); apr_status_t rv; apr_pool_create(&ptrans, pcgi); apr_signal(SIGCHLD, SIG_IGN); apr_signal(SIGHUP, daemon_signal_handler); /* Close our copy of the listening sockets */ ap_close_listeners(); /* cgid should use its own suexec doer */ ap_hook_get_suexec_identity(cgid_suexec_id_doer, NULL, NULL, APR_HOOK_REALLY_FIRST); apr_hook_sort_all(); if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, "Couldn't create unix domain socket"); return errno; } memset(&unix_addr, 0, sizeof(unix_addr)); unix_addr.sun_family = AF_UNIX; apr_cpystrn(unix_addr.sun_path, sockname, sizeof unix_addr.sun_path); omask = umask(0077); /* so that only Apache can use socket */ rc = bind(sd, (struct sockaddr *)&unix_addr, sizeof(unix_addr)); umask(omask); /* can't fail, so can't clobber errno */ if (rc < 0) { ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, "Couldn't bind unix domain socket %s", sockname); return errno; } /* Not all flavors of unix use the current umask for AF_UNIX perms */ rv = apr_file_perms_set(sockname, APR_FPROT_UREAD|APR_FPROT_UWRITE|APR_FPROT_UEXECUTE); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, main_server, "Couldn't set permissions on unix domain socket %s", sockname); return rv; } if (listen(sd, DEFAULT_CGID_LISTENBACKLOG) < 0) { ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, "Couldn't listen on unix domain socket"); return errno; } if (!geteuid()) { if (chown(sockname, unixd_config.user_id, -1) < 0) { ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, "Couldn't change owner of unix domain socket %s", sockname); return errno; } } unixd_setup_child(); /* if running as root, switch to configured user/group */ while (!daemon_should_exit) { int errfileno = STDERR_FILENO; char *argv0; char **env; const char * const *argv; apr_int32_t in_pipe; apr_int32_t out_pipe; apr_int32_t err_pipe; apr_cmdtype_e cmd_type; request_rec *r; apr_procattr_t *procattr = NULL; apr_proc_t *procnew = NULL; apr_file_t *inout; cgid_req_t cgid_req; apr_status_t stat; apr_pool_clear(ptrans); len = sizeof(unix_addr); sd2 = accept(sd, (struct sockaddr *)&unix_addr, &len); if (sd2 < 0) {#if defined(ENETDOWN) if (errno == ENETDOWN) { /* The network has been shut down, no need to continue. Die gracefully */ ++daemon_should_exit; }#endif if (errno != EINTR) { ap_log_error(APLOG_MARK, APLOG_ERR, errno, (server_rec *)data, "Error accepting on cgid socket"); } continue; } r = apr_pcalloc(ptrans, sizeof(request_rec)); procnew = apr_pcalloc(ptrans, sizeof(*procnew)); r->pool = ptrans; stat = get_req(sd2, r, &argv0, &env, &cgid_req); if (stat != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, stat, main_server, "Error reading request on cgid socket"); close(sd2); continue; } if (cgid_req.ppid != parent_pid) { ap_log_error(APLOG_MARK, APLOG_CRIT, 0, main_server, "CGI request received from wrong server instance; " "see ScriptSock directive"); close(sd2); continue; } if (cgid_req.req_type == GETPID_REQ) { pid_t pid; pid = (pid_t)((long)apr_hash_get(script_hash, &cgid_req.conn_id, sizeof(cgid_req.conn_id))); if (write(sd2, &pid, sizeof(pid)) != sizeof(pid)) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server, "Error writing pid %" APR_PID_T_FMT " to handler", pid); } close(sd2); continue; } apr_os_file_put(&r->server->error_log, &errfileno, 0, r->pool); apr_os_file_put(&inout, &sd2, 0, r->pool); if (cgid_req.req_type == SSI_REQ) { in_pipe = APR_NO_PIPE; out_pipe = APR_FULL_BLOCK; err_pipe = APR_NO_PIPE; cmd_type = APR_SHELLCMD; } else { in_pipe = APR_CHILD_BLOCK; out_pipe = APR_CHILD_BLOCK; err_pipe = APR_CHILD_BLOCK; cmd_type = APR_PROGRAM; } if (((rc = apr_procattr_create(&procattr, ptrans)) != APR_SUCCESS) || ((cgid_req.req_type == CGI_REQ) && (((rc = apr_procattr_io_set(procattr, in_pipe, out_pipe, err_pipe)) != APR_SUCCESS) || /* XXX apr_procattr_child_*_set() is creating an unnecessary * pipe between this process and the child being created... * It is cleaned up with the temporary pool for this request. */ ((rc = apr_procattr_child_err_set(procattr, r->server->error_log, NULL)) != APR_SUCCESS) || ((rc = apr_procattr_child_in_set(procattr, inout, NULL)) != APR_SUCCESS))) || ((rc = apr_procattr_child_out_set(procattr, inout, NULL)) != APR_SUCCESS) || ((rc = apr_procattr_dir_set(procattr, ap_make_dirstr_parent(r->pool, r->filename))) != APR_SUCCESS) || ((rc = apr_procattr_cmdtype_set(procattr, cmd_type)) != APR_SUCCESS) || ((rc = apr_procattr_child_errfn_set(procattr, cgid_child_errfn)) != APR_SUCCESS)) { /* Something bad happened, tell the world. * ap_log_rerror() won't work because the header table used by * ap_log_rerror() hasn't been replicated in the phony r */ ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server, "couldn't set child process attributes: %s", r->filename); } else { apr_pool_userdata_set(r, ERRFN_USERDATA_KEY, apr_pool_cleanup_null, ptrans); argv = (const char * const *)create_argv(r->pool, NULL, NULL, NULL, argv0, r->args); /* We want to close sd2 for the new CGI process too. * If it is left open it'll make ap_pass_brigade() block * waiting for EOF if CGI forked something running long. * close(sd2) here should be okay, as CGI channel * is already dup()ed by apr_procattr_child_{in,out}_set() * above. */ close(sd2); if (memcmp(&empty_ugid, &cgid_req.ugid, sizeof(empty_ugid))) { /* We have a valid identity, and can be sure that * cgid_suexec_id_doer will return a valid ugid */ rc = ap_os_create_privileged_process(r, procnew, argv0, argv, (const char * const *)env, procattr, ptrans); } else { rc = apr_proc_create(procnew, argv0, argv, (const char * const *)env, procattr, ptrans); } if (rc != APR_SUCCESS) { /* Bad things happened. Everyone should have cleaned up. * ap_log_rerror() won't work because the header table used by * ap_log_rerror() hasn't been replicated in the phony r */ ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server, "couldn't create child process: %d: %s", rc, apr_filepath_name_get(r->filename)); } else { /* We don't want to leak storage for the key, so only allocate * a key if the key doesn't exist yet in the hash; there are * only a limited number of possible keys (one for each * possible thread in the server), so we can allocate a copy * of the key the first time a thread has a cgid request. * Note that apr_hash_set() only uses the storage passed in * for the key if it is adding the key to the hash for the * first time; new key storage isn't needed for replacing the * existing value of a key. */ void *key; if (apr_hash_get(script_hash, &cgid_req.conn_id, sizeof(cgid_req.conn_id))) { key = &cgid_req.conn_id; } else { key = apr_pcalloc(pcgi, sizeof(cgid_req.conn_id)); memcpy(key, &cgid_req.conn_id, sizeof(cgid_req.conn_id)); } apr_hash_set(script_hash, key, sizeof(cgid_req.conn_id), (void *)((long)procnew->pid)); } } } return -1; /* should be <= 0 to distinguish from startup errors */}static int cgid_start(apr_pool_t *p, server_rec *main_server, apr_proc_t *procnew){ daemon_should_exit = 0; /* clear setting from previous generation */ if ((daemon_pid = fork()) < 0) { ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, "mod_cgid: Couldn't spawn cgid daemon process"); return DECLINED; } else if (daemon_pid == 0) { if (pcgi == NULL) { apr_pool_create(&pcgi, p); } exit(cgid_server(main_server) > 0 ? DAEMON_STARTUP_ERROR : -1); } procnew->pid = daemon_pid; procnew->err = procnew->in = procnew->out = NULL; apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT);#if APR_HAS_OTHER_CHILD apr_proc_other_child_register(procnew, cgid_maint, procnew, NULL, p);#endif return OK;}static int cgid_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp){ sockname = ap_append_pid(pconf, DEFAULT_SOCKET, "."); return OK;}static int cgid_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *main_server){ apr_proc_t *procnew = NULL; int first_time = 0; const char *userdata_key = "cgid_init"; module **m; int ret = OK; void *data; root_server = main_server; root_pool = p; apr_pool_userdata_get(&data, userdata_key, main_server->process->pool); if (!data) { first_time = 1; procnew = apr_pcalloc(main_server->process->pool, sizeof(*procnew)); procnew->pid = -1; procnew->err = procnew->in = procnew->out = NULL; apr_pool_userdata_set((const void *)procnew, userdata_key, apr_pool_cleanup_null, main_server->process->pool); } else { procnew = data; } if (!first_time) { total_modules = 0; for (m = ap_preloaded_modules; *m != NULL; m++) total_modules++; parent_pid = getpid(); sockname = ap_server_root_relative(p, sockname); ret = cgid_start(p, main_server, procnew); if (ret != OK ) { return ret; } cgid_pfn_reg_with_ssi = APR_RETRIEVE_OPTIONAL_FN(ap_register_include_handler); cgid_pfn_gtv = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_get_tag_and_value); cgid_pfn_ps = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_parse_string); if ((cgid_pfn_reg_with_ssi) && (cgid_pfn_gtv) && (cgid_pfn_ps)) { /* Required by mod_include filter. This is how mod_cgid registers * with mod_include to provide processing of the exec directive. */ cgid_pfn_reg_with_ssi("exec", handle_exec); } } return ret;}static void *create_cgid_config(apr_pool_t *p, server_rec *s){ cgid_server_conf *c = (cgid_server_conf *) apr_pcalloc(p, sizeof(cgid_server_conf)); c->logname = NULL; c->logbytes = DEFAULT_LOGBYTES; c->bufbytes = DEFAULT_BUFBYTES; return c;}static void *merge_cgid_config(apr_pool_t *p, void *basev, void *overridesv){ cgid_server_conf *base = (cgid_server_conf *) basev, *overrides = (cgid_server_conf *) overridesv; return overrides->logname ? overrides : base;}static const char *set_scriptlog(cmd_parms *cmd, void *dummy, const char *arg){ server_rec *s = cmd->server;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -