static int connect_socket(const char *socketname, int fail) { int sock = socket(PF_UNIX, SOCK_STREAM, 0); if (sock == -1) failwith_perror("socket"); int err; BEGIN_PROTECTCWD struct sockaddr_un address; int address_len; chdir(path_socketdir()); address.sun_family = AF_UNIX; snprintf(address.sun_path, 104, "./%s", socketname); address_len = strlen(address.sun_path) + sizeof(address.sun_family) + 1; NO_EINTR(err, connect(sock, (struct sockaddr*)&address, address_len)); END_PROTECTCWD if (err == -1) { if (fail) failwith_perror("connect"); close(sock); return -1; } return sock; }
static void start_server(const char *socketname, const char* ignored, const char *exec_path) { int sock = socket(PF_UNIX, SOCK_STREAM, 0); if (sock == -1) failwith_perror("socket"); int err; BEGIN_PROTECTCWD struct sockaddr_un address; int address_len; chdir(path_socketdir()); address.sun_family = AF_UNIX; snprintf(address.sun_path, 104, "./%s", socketname); address_len = strlen(address.sun_path) + sizeof(address.sun_family) + 1; unlink(address.sun_path); NO_EINTR(err, bind(sock, (struct sockaddr*)&address, address_len)); END_PROTECTCWD if (err == -1) failwith_perror("bind"); if (listen(sock, 5) == -1) failwith_perror("listen"); pid_t child = fork(); if (child == -1) failwith_perror("fork"); if (child == 0) { make_daemon(sock); char socket_fd[50], socket_path[PATHSZ]; sprintf(socket_fd, "%d", sock); snprintf(socket_path, PATHSZ, "%s/%s", path_socketdir(), socketname); //execlp("nohup", "nohup", exec_path, "server", socket_path, socket_fd, NULL); execlp(exec_path, exec_path, "server", socket_path, socket_fd, NULL); failwith_perror("execlp"); } close(sock); wait(NULL); }
value ml_merlin_context_close(value context, value return_code) { CAMLparam1(context); setup_fds(-1, -1, -1); char code = (char)(Int_val(return_code)); ssize_t wrote_ = -1; NO_EINTR(wrote_, write(Int_val(Field(context, 0)), &code, sizeof(char))); // Close stdin, stdout, stderr close(Int_val(Field(context, 1))); close(Int_val(Field(context, 2))); close(Int_val(Field(context, 3))); // Close client connection close(Int_val(Field(context, 0))); CAMLreturn(Val_unit); }
int main(int argc, char **argv) { char result = 0; int err = 0; struct stat st; #ifdef _WIN32 HANDLE fds[3]; ULONG pid; HANDLE hProcess, hServerProcess; DWORD dwNumberOfBytesRead; CHAR argv0[PATHSZ]; GetModuleFileName(NULL, argv0, PATHSZ); compute_merlinpath(merlin_path, argv0, &st); #else compute_merlinpath(merlin_path, argv[0], &st); #endif if (argc >= 2 && strcmp(argv[1], "server") == 0) { IPC_SOCKET_TYPE sock; ssize_t len; #ifdef _WIN32 compute_socketname(socketname, eventname, merlin_path); #else compute_socketname(socketname, &st); #endif sock = connect_and_serve(socketname, eventname, merlin_path); len = prepare_args(argbuffer, sizeof(argbuffer), argc-2, argv+2); #ifdef _WIN32 hProcess = GetCurrentProcess(); if (!GetNamedPipeServerProcessId(sock, &pid)) failwith_perror("GetNamedPipeServerProcessId"); hServerProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid); if (hServerProcess == INVALID_HANDLE_VALUE) failwith_perror("OpenProcess"); if (!DuplicateHandle(hProcess, GetStdHandle(STD_INPUT_HANDLE), hServerProcess, &fds[0], 0, FALSE, DUPLICATE_SAME_ACCESS)) failwith_perror("DuplicateHandle(stdin)"); if (!DuplicateHandle(hProcess, GetStdHandle(STD_OUTPUT_HANDLE), hServerProcess, &fds[1], 0, FALSE, DUPLICATE_SAME_ACCESS)) failwith_perror("DuplicateHandle(stdout)"); CloseHandle(GetStdHandle(STD_OUTPUT_HANDLE)); if (!DuplicateHandle(hProcess, GetStdHandle(STD_ERROR_HANDLE), hServerProcess, &fds[2], 0, FALSE, DUPLICATE_SAME_ACCESS)) failwith_perror("DuplicateHandle(stderr)"); #else int fds[3] = { STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO }; #endif ipc_send(sock, argbuffer, len, fds); #ifdef _WIN32 if (ReadFile(sock, &result, 1, &dwNumberOfBytesRead, NULL) && dwNumberOfBytesRead == 1) err = 1; #else NO_EINTR(err, read(sock, &result, 1)); #endif if (err == 1) exit(result); unexpected_termination(argc, argv); } else { argv[0] = ocamlmerlin_server; execvp(merlin_path, argv); failwith_perror("execvp(ocamlmerlin-server)"); } }
static void ipc_send(int fd, unsigned char *buffer, size_t len, int fds[3]) { char msg_control[CMSG_SPACE(3 * sizeof(int))]; struct iovec iov = { .iov_base = buffer, .iov_len = len }; struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1, .msg_controllen = CMSG_SPACE(3 * sizeof(int)), }; msg.msg_control = &msg_control; memset(msg.msg_control, 0, msg.msg_controllen); struct cmsghdr *cm = CMSG_FIRSTHDR(&msg); cm->cmsg_level = SOL_SOCKET; cm->cmsg_type = SCM_RIGHTS; cm->cmsg_len = CMSG_LEN(3 * sizeof(int)); int *fds0 = (int*)CMSG_DATA(cm); fds0[0] = fds[0]; fds0[1] = fds[1]; fds0[2] = fds[2]; ssize_t sent; NO_EINTR(sent, sendmsg(fd, &msg, 0)); if (sent == -1) failwith_perror("sendmsg"); while (sent < len) { ssize_t sent_; NO_EINTR(sent_, send(fd, buffer + sent, len - sent, 0)); if (sent_ == -1) failwith_perror("sent"); sent += sent_; } } #endif /* Serialize arguments */ #define byte(x,n) ((unsigned)((x) >> (n * 8)) & 0xFF) static void append_argument(unsigned char *buffer, size_t len, ssize_t *pos, const char *p) { ssize_t j = *pos; while (*p && j < len) { buffer[j] = *p; j += 1; p += 1; } if (j >= len) failwith("maximum number of arguments exceeded"); buffer[j] = 0; j += 1; *pos = j; }
static ssize_t recv_buffer(int fd, int fds[3]) { struct iovec iov = { .iov_base = buffer, .iov_len = sizeof(buffer) }; struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1, .msg_controllen = CMSG_SPACE(3 * sizeof(int)), }; msg.msg_control = alloca(msg.msg_controllen); memset(msg.msg_control, 0, msg.msg_controllen); ssize_t recvd; NO_EINTR(recvd, recvmsg(fd, &msg, 0)); if (recvd == -1) { perror("recvmsg"); return -1; } if (recvd < 4) { ssize_t recvd_; do { NO_EINTR(recvd_, recv(fd, buffer + recvd, sizeof(buffer) - recvd, 0)); if (recvd_ > 0) recvd += recvd_; } while (recvd_ > 0 && recvd < 4); } size_t target = -1; if (recvd > 4) { target = unbyte(buffer[0],0) | unbyte(buffer[1],1) | unbyte(buffer[2],2) | unbyte(buffer[3],3); if (recvd < target) { ssize_t recvd_; do { NO_EINTR(recvd_, recv(fd, buffer + recvd, sizeof(buffer) - recvd, 0)); if (recvd_ > 0) recvd += recvd_; } while (recvd_ > 0 && recvd < target); } } struct cmsghdr *cm = CMSG_FIRSTHDR(&msg); int *fds0 = (int*)CMSG_DATA(cm); int nfds = (cm->cmsg_len - CMSG_LEN(0)) / sizeof(int); /* Check malformed packet */ if (nfds != 3 || recvd != target || buffer[recvd-1] != '\0') { int i; for (i = 0; i < nfds; ++i) close(fds0[i]); return -1; } fds[0] = fds0[0]; fds[1] = fds0[1]; fds[2] = fds0[2]; return recvd; } value ml_merlin_server_setup(value path, value strfd) { CAMLparam2(path, strfd); CAMLlocal2(payload, ret); char *endptr = NULL; int fd = strtol(String_val(strfd), &endptr, 0); if (endptr && *endptr == '\0') { /* (path, fd) */ payload = caml_alloc(2, 0); Store_field(payload, 0, path); Store_field(payload, 1, Val_int(fd)); /* Some payload */ ret = caml_alloc(1, 0); Store_field(ret, 0, payload); } else { fprintf(stderr, "ml_merlin_server_setup(\"%s\",\"%s\"): invalid argument\n", String_val(path), String_val(strfd)); unlink(String_val(path)); /* None */ ret = Val_unit; } CAMLreturn(ret); } value ml_merlin_server_accept(value server, value val_timeout) { CAMLparam2(server, val_timeout); CAMLlocal4(ret, client, args, context); // Compute timeout double timeout = Double_val(val_timeout); struct timeval tv; tv.tv_sec = timeout; tv.tv_usec = (timeout - tv.tv_sec) * 1000000; // Select on server int serverfd = Int_val(Field(server, 1)); int selectres; fd_set readset; do { FD_ZERO(&readset); FD_SET(serverfd, &readset); selectres = select(serverfd + 1, &readset, NULL, NULL, &tv); } while (selectres == -1 && errno == EINTR); int fds[3], clientfd; ssize_t len = -1; if (selectres > 0) { NO_EINTR(clientfd, accept(serverfd, NULL, NULL)); len = recv_buffer(clientfd, fds); } if (len == -1) ret = Val_unit; /* None */ else { context = caml_alloc(4, 0); /* (clientfd, stdin, stdout, stderr) */ Store_field(context, 0, Val_int(clientfd)); Store_field(context, 1, Val_int(fds[0])); Store_field(context, 2, Val_int(fds[1])); Store_field(context, 3, Val_int(fds[2])); ssize_t i, j; int argc = 0; for (i = 4; i < len; ++i) if (buffer[i] == '\0') argc += 1; args = caml_alloc(argc, 0); argc = 0; for (i = 4, j = 4; i < len; ++i) { if (buffer[i] == '\0') { Store_field(args, argc, caml_copy_string((const char *)&buffer[j])); j = i + 1; argc += 1; } } client = caml_alloc(2, 0); /* (context, args) */ Store_field(client, 0, context); Store_field(client, 1, args); ret = caml_alloc(1, 0); /* Some client */ Store_field(ret, 0, client); } CAMLreturn(ret); }