/*
* 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 */