static void systray(void *arg) { WNDCLASS cls; HWND hWnd; MSG msg; (void) memset(&cls, 0, sizeof(cls)); cls.lpfnWndProc = (WNDPROC) WindowProc; cls.hIcon = LoadIcon(NULL, IDI_APPLICATION); cls.lpszClassName = "shttpd v." VERSION; if (!RegisterClass(&cls)) _shttpd_elog(E_FATAL, NULL, "RegisterClass: %d", ERRNO); else if ((hWnd = CreateWindow(cls.lpszClassName, "", WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, NULL, NULL, NULL, arg)) == NULL) _shttpd_elog(E_FATAL, NULL, "CreateWindow: %d", ERRNO); ShowWindow(hWnd, SW_HIDE); ni.cbSize = sizeof(ni); ni.uID = ID_TRAYICON; ni.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; ni.hIcon = LoadIcon(NULL, IDI_APPLICATION); ni.hWnd = hWnd; _shttpd_snprintf(ni.szTip, sizeof(ni.szTip), "SHTTPD web server"); ni.uCallbackMessage = WM_USER; Shell_NotifyIcon(NIM_ADD, &ni); while (GetMessage(&msg, hWnd, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } }
int _shttpd_spawn_process(struct conn *c, const char *prog, char *envblk, char *envp[], int sock, const char *dir) { int ret; pid_t pid; const char *p, *interp = c->ctx->options[OPT_CGI_INTERPRETER]; envblk = NULL; /* unused */ if ((pid = vfork()) == -1) { ret = -1; _shttpd_elog(E_LOG, c, "redirect: fork: %s", strerror(errno)); } else if (pid == 0) { /* Child */ (void) chdir(dir); (void) dup2(sock, 0); (void) dup2(sock, 1); (void) closesocket(sock); /* If error file is specified, send errors there */ if (c->ctx->error_log) (void) dup2(fileno(c->ctx->error_log), 2); if ((p = strrchr(prog, '/')) != NULL) p++; else p = prog; /* Execute CGI program */ if (interp == NULL) { (void) execle(p, p, NULL, envp); _shttpd_elog(E_FATAL, c, "redirect: exec(%s)", prog); } else { (void) execle(interp, interp, p, NULL, envp); _shttpd_elog(E_FATAL, c, "redirect: exec(%s %s)", interp, prog); } /* UNREACHED */ exit(EXIT_FAILURE); } else { /* Parent */ ret = 0; (void) closesocket(sock); } return (ret); }
static void WINAPI ServiceMain(int argc, char *argv[]) { char path[MAX_PATH], *p, *av[] = {"shttpd_service", path, NULL}; struct shttpd_ctx *ctx; ss.dwServiceType = SERVICE_WIN32; ss.dwCurrentState = SERVICE_RUNNING; ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; hStatus = RegisterServiceCtrlHandler(SERVICE_NAME, ControlHandler); SetServiceStatus(hStatus, &ss); GetModuleFileName(NULL, path, sizeof(path)); if ((p = strrchr(path, DIRSEP)) != NULL) *++p = '\0'; strcat(path, CONFIG_FILE); /* woo ! */ ctx = shttpd_init(NELEMS(av) - 1, av); if ((ctx = shttpd_init(NELEMS(av) - 1, av)) == NULL) _shttpd_elog(E_FATAL, NULL, "Cannot initialize SHTTP context"); while (ss.dwCurrentState == SERVICE_RUNNING) shttpd_poll(ctx, INT_MAX); shttpd_fini(ctx); ss.dwCurrentState = SERVICE_STOPPED; ss.dwWin32ExitCode = -1; SetServiceStatus(hStatus, &ss); }
int _shttpd_set_nt_service(struct shttpd_ctx *ctx, const char *action) { SC_HANDLE hSCM, hService; char path[FILENAME_MAX], key[128]; HKEY hKey; DWORD dwData; if (!strcmp(action, "install")) { if ((hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL) _shttpd_elog(E_FATAL, NULL, "Error opening SCM (%d)", ERRNO); GetModuleFileName(NULL, path, sizeof(path)); hService = CreateService(hSCM, SERVICE_NAME, SERVICE_NAME, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, path, NULL, NULL, NULL, NULL, NULL); if (!hService) _shttpd_elog(E_FATAL, NULL, "Error installing service (%d)", ERRNO); ChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &service_descr); _shttpd_elog(E_FATAL, NULL, "Service successfully installed"); } else if (!strcmp(action, "uninstall")) { if ((hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL) { _shttpd_elog(E_FATAL, NULL, "Error opening SCM (%d)", ERRNO); } else if ((hService = OpenService(hSCM, SERVICE_NAME, DELETE)) == NULL) { _shttpd_elog(E_FATAL, NULL, "Error opening service (%d)", ERRNO); } else if (!DeleteService(hService)) { _shttpd_elog(E_FATAL, NULL, "Error deleting service (%d)", ERRNO); } else { _shttpd_elog(E_FATAL, NULL, "Service deleted"); } } else { _shttpd_elog(E_FATAL, NULL, "Use -service <install|uninstall>"); } /* NOTREACHED */ return (TRUE); }
int main(int argc, char *argv[]) { struct shttpd_ctx *ctx; #if !defined(NO_AUTH) if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'A') { if (argc != 6) _shttpd_usage(argv[0]); exit(_shttpd_edit_passwords(argv[2],argv[3],argv[4],argv[5])); } #endif /* NO_AUTH */ if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) _shttpd_usage(argv[0]); #if defined(_WIN32) try_to_run_as_nt_service(); #endif /* _WIN32 */ #ifndef _WIN32 (void) signal(SIGCHLD, signal_handler); (void) signal(SIGPIPE, SIG_IGN); #endif /* _WIN32 */ (void) signal(SIGTERM, signal_handler); (void) signal(SIGINT, signal_handler); if ((ctx = shttpd_init(argc, argv)) == NULL) _shttpd_elog(E_FATAL, NULL, "%s", "Cannot initialize SHTTPD context"); _shttpd_elog(E_LOG, NULL, "shttpd %s started on port(s) %s, serving %s", VERSION, ctx->options[OPT_PORTS], ctx->options[OPT_ROOT]); while (exit_flag == 0) shttpd_poll(ctx, 10 * 1000); _shttpd_elog(E_LOG, NULL, "Exit on signal %d", exit_flag); shttpd_fini(ctx); return (EXIT_SUCCESS); }
static int read_cgi(struct stream *stream, void *buf, size_t len) { struct headers parsed; char status[4]; int n; assert(stream->chan.sock != -1); assert(stream->flags & FLAG_R); stream->flags &= ~FLAG_DONT_CLOSE; n = recv(stream->chan.sock, buf, len, 0); if (stream->flags & FLAG_HEADERS_PARSED) return (n); if (n <= 0 && ERRNO != EWOULDBLOCK) { _shttpd_send_server_error(stream->conn, 500, "Error running CGI"); return (n); } /* * CGI script may output Status: and Location: headers, which * may alter the status code. Buffer in headers, parse * them, send correct status code and then forward all data * from CGI script back to the remote end. * Reply line was alredy appended to the IO buffer in * decide_what_to_do(), with blank status code. */ stream->flags |= FLAG_DONT_CLOSE; io_inc_head(&stream->io, n); stream->headers_len = _shttpd_get_headers_len(stream->io.buf, stream->io.head); if (stream->headers_len < 0) { stream->flags &= ~FLAG_DONT_CLOSE; _shttpd_send_server_error(stream->conn, 500, "Bad headers sent"); _shttpd_elog(E_LOG, stream->conn, "CGI script sent invalid headers: " "[%.*s]", stream->io.head - CGI_REPLY_LEN, stream->io.buf + CGI_REPLY_LEN); return (0); } /* * If we did not received full headers yet, we must not send any * data read from the CGI back to the client. Suspend sending by * setting tail = head, which tells that there is no data in IO buffer */ if (stream->headers_len == 0) { stream->io.tail = stream->io.head; return (0); } /* Received all headers. Set status code for the connection. */ (void) memset(&parsed, 0, sizeof(parsed)); _shttpd_parse_headers(stream->io.buf, stream->headers_len, &parsed); stream->content_len = parsed.cl.v_big_int; stream->conn->status = (int) parsed.status.v_big_int; /* If script outputs 'Location:' header, set status code to 302 */ if (parsed.location.v_vec.len > 0) stream->conn->status = 302; /* * If script did not output neither 'Location:' nor 'Status' headers, * set the default status code 200, which means 'success'. */ if (stream->conn->status == 0) stream->conn->status = 200; /* Append the status line to the beginning of the output */ (void) _shttpd_snprintf(status, sizeof(status), "%3d", stream->conn->status); (void) memcpy(stream->io.buf + 9, status, 3); DBG(("read_cgi: content len %lu status %s", stream->content_len, status)); /* Next time, pass output directly back to the client */ assert((big_int_t) stream->headers_len <= stream->io.total); stream->io.total -= stream->headers_len; stream->io.tail = 0; stream->flags |= FLAG_HEADERS_PARSED; /* Return 0 because we've already shifted the head */ return (0); }
int _shttpd_spawn_process(struct conn *c, const char *prog, char *envblk, char *envp[], int sock, const char *dir) { HANDLE a[2], b[2], h[2], me; DWORD flags; char *p, *interp, cmdline[FILENAME_MAX], line[FILENAME_MAX]; FILE *fp; STARTUPINFOA si; PROCESS_INFORMATION pi; me = GetCurrentProcess(); flags = DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS; /* FIXME add error checking code here */ CreatePipe(&a[0], &a[1], NULL, 0); CreatePipe(&b[0], &b[1], NULL, 0); DuplicateHandle(me, a[0], me, &h[0], 0, TRUE, flags); DuplicateHandle(me, b[1], me, &h[1], 0, TRUE, flags); (void) memset(&si, 0, sizeof(si)); (void) memset(&pi, 0, sizeof(pi)); /* XXX redirect CGI errors to the error log file */ si.cb = sizeof(si); si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; si.hStdOutput = h[1]; si.hStdInput = h[0]; /* If CGI file is a script, try to read the interpreter line */ interp = c->ctx->options[OPT_CGI_INTERPRETER]; if (interp == NULL) { if ((fp = fopen(prog, "r")) != NULL) { (void) fgets(line, sizeof(line), fp); if (memcmp(line, "#!", 2) != 0) line[2] = '\0'; /* Trim whitespaces from interpreter name */ for (p = &line[strlen(line) - 1]; p > line && isspace(*p); p--) *p = '\0'; (void) fclose(fp); } interp = line + 2; (void) _shttpd_snprintf(cmdline, sizeof(cmdline), "%s%s%s", line + 2, line[2] == '\0' ? "" : " ", prog); } if ((p = strrchr(prog, '/')) != NULL) prog = p + 1; (void) _shttpd_snprintf(cmdline, sizeof(cmdline), "%s %s", interp, prog); (void) _shttpd_snprintf(line, sizeof(line), "%s", dir); fix_directory_separators(line); fix_directory_separators(cmdline); /* * Spawn reader & writer threads before we create CGI process. * Otherwise CGI process may die too quickly, loosing the data */ spawn_stdio_thread(sock, b[0], stdinput, 0); spawn_stdio_thread(sock, a[1], stdoutput, c->rem.content_len); if (CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP, envblk, line, &si, &pi) == 0) { _shttpd_elog(E_LOG, c, "redirect: CreateProcess(%s): %d", cmdline, ERRNO); return (-1); } else { CloseHandle(h[0]); CloseHandle(h[1]); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); } return (0); }