/* * Copyright (c) 2007-2017 Hypertriton, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #ifdef HAVE_CONTROL #include #include #include #include #include #include #include #include #include #include #include #include "mailprocd.h" #include "pathnames.h" #include "peercred.h" char *ctlSockPath; /* Path to control socket */ Uint ctlClientCount, ctlMaxClients; /* Concurrent sessions */ struct ctl_session_q ctlSessions; const char *ctlCommandNames[] = { "CTL_VERSION", "CTL_VERSION_SA", NULL }; void CTL_Init(CFG_File *cf) { TAILQ_INIT(&ctlSessions); ctlClientCount = 0; CFG_GetStr(cf, "ctl.socket-path", &ctlSockPath, _PATH_CTL_SOCKET); CFG_GetUint(cf, "ctl.max-clients", &ctlMaxClients, 30); } void CTL_Destroy(void) { CTL_Session *sess, *sessNext; for (sess = TAILQ_FIRST(&ctlSessions); sess != TAILQ_END(&ctlSessions); sess = sessNext) { sessNext = TAILQ_NEXT(sess, sessions); free(sess); } TAILQ_INIT(&ctlSessions); unlink(ctlSockPath); } void CTL_ForkInstance(int sock) { pid_t pid; if (ctlClientCount+1 > ctlMaxClients) { syslog(LOG_ERR, "CTL: Too many clients (%d)", ctlMaxClients); return; } if ((pid = fork()) != 0) { CTL_Session *sess; sess = Malloc(sizeof(CTL_Session)); sess->pid = pid; TAILQ_INSERT_TAIL(&ctlSessions, sess, sessions); Debug("New control session: %d (session #%d/%d)", (int)pid, ctlClientCount, ctlMaxClients); ctlClientCount++; } else { dup2(sock, STDIN_FILENO); dup2(sock, STDOUT_FILENO); MPD_EnterServerProc("control"); setvbuf(stdout, NULL, _IONBF, 0); CTL_Main(); MPD_ExitServerProc(0); } } static void ServerResponse(int rv, const char *errMsg) { char code = (rv == 0) ? '0' : '!'; char errBuf[CTL_ERROR_MAX]; write(STDOUT_FILENO, &code, 1); if (rv != 0) { memset(errBuf, '\0', sizeof(errBuf)); Strlcpy(errBuf, errMsg, sizeof(errBuf)); write(STDOUT_FILENO, errBuf, sizeof(errBuf)); } } void CTL_Main(void) { char cmd; if (write(STDOUT_FILENO, "mailprocd\n", 10) != 10) { goto fail_write; } if (read(STDIN_FILENO, &cmd, 1) != 1) { goto fail_read; } switch (cmd) { case CTL_VERSION: write(STDOUT_FILENO, VERSION, sizeof(VERSION)); write(STDOUT_FILENO, "\n", 1); write(STDOUT_FILENO, RELEASE, sizeof(RELEASE)); write(STDOUT_FILENO, "\n", 1); break; case CTL_VERSION_SA: #ifdef HAVE_SA write(STDOUT_FILENO, saVersion, strlen(saVersion)); write(STDOUT_FILENO, "\n", 1); #else ServerResponse(-1, "Not available"); #endif break; default: ServerResponse(-1, "Unimplemented command"); Debug("Bad command: 0x%x", cmd); syslog(LOG_ERR, "Invalid command 0x%x on control socket", cmd); break; } return; fail_write: syslog(LOG_ERR, "Write error on control socket"); return; fail_read: syslog(LOG_ERR, "Read error on control socket"); return; } /* Issue a request on the control socket. */ int CTL_SendCommand(enum ctl_command cmd, ...) { char signature[11]; char errMsg[CTL_ERROR_MAX]; struct sockaddr_un sun; my_socklen_t socklen; unsigned char cmdData, errCode; int s; Debug("Sending %s", ctlCommandNames[cmd]); if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { MPD_SetError("socket: %s", strerror(errno)); return (-1); } sun.sun_family = AF_UNIX; Strlcpy(sun.sun_path, _PATH_CTL_SOCKET, sizeof(sun.sun_path)); socklen = SUN_LEN(&sun); if (connect(s, (struct sockaddr *)&sun, socklen) == -1) { MPD_SetError("%s: %s", _PATH_CTL_SOCKET, strerror(errno)); return (-1); } if (read(s, signature, 10) != 10) { MPD_SetErrorS("Error reading signature"); goto fail; } signature[10] = '\0'; if (strcmp(signature, "mailprocd\n") != 0) { MPD_SetErrorS("Bad signature"); goto fail; } cmdData = (unsigned char)cmd; if (write(s, &cmdData, 1) != 1) { MPD_SetError("Error writing %s", ctlCommandNames[cmd]); goto fail; } if (read(s, &errCode, 1) != 1) { MPD_SetErrorS("Error reading response code"); goto fail; } if (errCode == '!') { if (read(s, errMsg, sizeof(errMsg)) != sizeof(errMsg)) { MPD_SetErrorS("Error reading error message"); goto fail; } errMsg[CTL_ERROR_MAX-1] = '\0'; MPD_SetError("Server: %s", errMsg); goto fail; } close(s); Debug("Sent %s command", ctlCommandNames[cmd]); return (0); fail: close(s); return (-1); } #endif /* HAVE_CONTROL */