50a0659f |
/*
* $Id$
*
* UNIX Domain Socket Server
*
|
53c7e0f1 |
* Copyright (C) 2001-2004 FhG Fokus
|
50a0659f |
*
* This file is part of ser, a free SIP server.
*
* ser is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version
*
* For a license to use the ser software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* ser is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
|
851b529f |
/* History:
* created by janakj
* 2004-03-03 added tcp init code (andrei)
|
71fd3ebd |
* 2004-04-29 added chmod(sock_perm) & chown(sock_user,sock_group) (andrei)
|
851b529f |
*/
|
50a0659f |
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
|
4421b319 |
#include <signal.h>
|
50a0659f |
#include <stdarg.h>
|
4421b319 |
#include <time.h>
|
037024d7 |
#include <fcntl.h>
|
4421b319 |
#include "config.h"
|
50a0659f |
#include "ut.h"
#include "globals.h"
#include "trim.h"
#include "pt.h"
#include "sr_module.h"
#include "mem/mem.h"
#include "fifo_server.h" /* CMD_SEPARATOR */
#include "unixsock_server.h"
|
372ed8d2 |
#include "tsend.h"
|
50a0659f |
|
852abad5 |
/* AF_LOCAL is not defined on solaris */
#if !defined(AF_LOCAL)
#define AF_LOCAL AF_UNIX
#endif
#if !defined(PF_LOCAL)
#define PF_LOCAL PF_UNIX
#endif
/* solaris doesn't have SUN_LEN */
#ifndef SUN_LEN
#define SUN_LEN(sa) ( strlen((sa)->sun_path) + \
(size_t)(((struct sockaddr_un*)0)->sun_path) )
#endif
|
50a0659f |
#define UNIXSOCK_BUF_SIZE BUF_SIZE
char* unixsock_name = 0;
int unixsock_children = 1;
|
53c7e0f1 |
int unixsock_tx_timeout = 2000; /* Timeout for sending replies in milliseconds */
|
6b17cae3 |
|
50a0659f |
|
037024d7 |
static int rx_sock, tx_sock;
|
22e71358 |
static struct unixsock_cmd* cmd_list = 0;
|
50a0659f |
static char reply_buf[UNIXSOCK_BUF_SIZE];
static str reply_pos;
static struct sockaddr_un reply_addr;
|
d7a3fdea |
static unsigned int reply_addr_len;
|
50a0659f |
|
4421b319 |
static time_t up_since;
static char up_since_ctime[MAX_CTIME_LEN];
|
50a0659f |
|
4421b319 |
#define PRINT_CMD "print" /* Diagnostic command */
#define VERSION_CMD "version" /* Print the version of the server */
#define UPTIME_CMD "uptime" /* Print server's uptime */
#define WHICH_CMD "which" /* Print available FIFO commands */
#define PS_CMD "ps" /* Print server's process table */
#define ARG_CMD "arg" /* Print server's command line arguments */
#define PWD_CMD "pwd" /* Get the current working directory */
#define KILL_CMD "kill" /* Kill the server */
/*
* Diagnostic and hello-world command
*/
static int print_cmd(str* msg)
{
str line;
int ret;
ret = 0;
if (unixsock_read_line(&line, msg) < 0) {
unixsock_reply_asciiz("500 Error while reading text\n");
ret = -1;
goto end;
}
if (unixsock_reply_printf("200 OK\n%.*s\n", line.len, ZSW(line.s)) < 0) {
unixsock_reply_reset();
unixsock_reply_asciiz("500 Error while sending reply\n");
ret = -1;
}
end:
if (unixsock_reply_send() < 0) ret = -1;
return ret;
}
/*
* Print the version of the server
*/
static int version_cmd(str* msg)
{
int ret;
ret = 0;
if (unixsock_reply_asciiz("200 OK\n" SERVER_HDR CRLF) < 0) ret = -1;
if (unixsock_reply_send() < 0) ret = -1;
return ret;
}
static int uptime_cmd(str* msg)
{
time_t now;
int ret;
time(&now);
ret = 0;
if (unixsock_reply_printf("200 OK\nNow: %sUp Since: %sUp time: %.0f [sec]\n",
ctime(&now), up_since_ctime, difftime(now, up_since)) < 0) {
unixsock_reply_reset();
unixsock_reply_asciiz("500 Error while printing reply\n");
ret = -1;
}
if (unixsock_reply_send() < 0) {
ret = -1;
}
return ret;
}
static int which_cmd(str* msg)
{
struct unixsock_cmd* c;
int ret;
ret = 0;
unixsock_reply_asciiz("200 OK\n");
for(c = cmd_list; c; c = c->next) {
if (unixsock_reply_printf("%s\n", c->name) < 0) {
unixsock_reply_reset();
unixsock_reply_asciiz("500 Error while creating reply\n");
ret = -1;
break;
}
}
if (unixsock_reply_send() < 0) {
ret = -1;
}
return ret;
}
static int ps_cmd(str* msg)
{
int p, ret;
ret = 0;
unixsock_reply_asciiz("200 OK\n");
for (p = 0; p < process_count(); p++) {
if (unixsock_reply_printf("%d\t%d\t%s\n", p, pt[p].pid, pt[p].desc) < 0) {
unixsock_reply_reset();
unixsock_reply_asciiz("500 Error while printing reply\n");
ret = -1;
break;
}
}
if (unixsock_reply_send() < 0) {
ret = -1;
}
return ret;
}
static int pwd_cmd(str* msg)
{
char *cwd_buf;
int max_len, ret;
max_len = pathmax();
cwd_buf = pkg_malloc(max_len);
ret = 0;
if (!cwd_buf) {
LOG(L_ERR, "pwd_cmd: No memory left\n");
unixsock_reply_asciiz("500 No Memory Left\n");
ret = -1;
}
if (getcwd(cwd_buf, max_len)) {
if (unixsock_reply_printf("200 OK\n%s\n", cwd_buf) < 0) {
unixsock_reply_reset();
unixsock_reply_asciiz("500 Error while sending reply\n");
ret = -1;
}
} else {
unixsock_reply_asciiz("500 getcwd Failed\n");
ret = -1;
}
pkg_free(cwd_buf);
if (unixsock_reply_send() < 0) {
ret = -1;
}
return ret;
}
static int arg_cmd(str* msg)
{
int p, ret;
ret = 0;
unixsock_reply_asciiz("200 OK\n");
for (p = 0; p < my_argc; p++) {
if (unixsock_reply_printf("%s\n", my_argv[p]) < 0) {
unixsock_reply_reset();
unixsock_reply_asciiz("500 Could not create reply\n");
ret = -1;
break;
}
}
if (unixsock_reply_send() < 0) {
ret = -1;
}
return ret;
}
static int kill_cmd(str* msg)
{
unixsock_reply_asciiz("200 Killing now\n");
unixsock_reply_send();
kill(0, SIGTERM);
return 0;
}
static int register_core_commands(void)
{
if (unixsock_register_cmd(PRINT_CMD, print_cmd) < 0) {
return -1;
}
if (unixsock_register_cmd(VERSION_CMD, version_cmd) < 0) {
return -1;
}
if (unixsock_register_cmd(UPTIME_CMD, uptime_cmd) < 0) {
return -1;
}
if (unixsock_register_cmd(WHICH_CMD, which_cmd) < 0) {
return -1;
}
if (unixsock_register_cmd(PS_CMD, ps_cmd) < 0) {
return -1;
}
if (unixsock_register_cmd(PWD_CMD, pwd_cmd) < 0) {
return -1;
}
if (unixsock_register_cmd(ARG_CMD, arg_cmd) < 0) {
return -1;
}
if (unixsock_register_cmd(KILL_CMD, kill_cmd) < 0) {
return -1;
}
return 0;
}
|
50a0659f |
/*
* Create and bind local socket
*/
|
b3fef92b |
int init_unixsock_socket(void)
|
50a0659f |
{
struct sockaddr_un addr;
|
037024d7 |
int len, flags;
|
50a0659f |
|
b3fef92b |
if (unixsock_name == 0) {
DBG("init_unixsock_socket: No unix domain socket"
|
50a0659f |
" will be opened\n");
return 1;
}
|
b3fef92b |
len = strlen(unixsock_name);
|
50a0659f |
if (len == 0) {
|
b3fef92b |
DBG("init_unixsock_socket: Unix domain socket server disabled\n");
|
50a0659f |
return 1;
} else if (len > 107) {
|
71fd3ebd |
LOG(L_ERR, "ERROR: init_unixsock_socket: Socket name too long\n");
|
50a0659f |
return -1;
}
|
b3fef92b |
DBG("init_unixsock_socket: Initializing Unix domain socket server @ %s\n",
unixsock_name);
|
50a0659f |
|
b3fef92b |
if (unlink(unixsock_name) == -1) {
|
50a0659f |
if (errno != ENOENT) {
|
71fd3ebd |
LOG(L_ERR, "ERROR: init_unixsock_socket: Error while unlinking "
|
b3fef92b |
"old socket (%s): %s\n", unixsock_name, strerror(errno));
|
50a0659f |
return -1;
}
}
|
037024d7 |
rx_sock = socket(PF_LOCAL, SOCK_DGRAM, 0);
if (rx_sock == -1) {
|
71fd3ebd |
LOG(L_ERR, "ERROR: init_unixsock_socket: Cannot create RX "
"socket: %s\n", strerror(errno));
|
50a0659f |
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = PF_LOCAL;
|
b3fef92b |
memcpy(addr.sun_path, unixsock_name, len);
|
50a0659f |
|
037024d7 |
if (bind(rx_sock, (struct sockaddr*)&addr, SUN_LEN(&addr)) == -1) {
|
71fd3ebd |
LOG(L_ERR, "ERROR: init_unixsock_socket: bind: %s\n", strerror(errno));
|
037024d7 |
goto err_rx;
}
|
71fd3ebd |
/* try to change the permissions */
if (sock_mode){ /* sock_mode==0 doesn't make sense, nobody can read/write*/
if (chmod(unixsock_name, sock_mode)<0){
LOG(L_ERR, "ERROR: init_unixsock_socket: failed to change the"
" permissions for %s to %04o: %s[%d]\n",
unixsock_name, sock_mode, strerror(errno), errno);
goto err_rx;
}
}
/* try to change the ownership */
if ((sock_uid!=-1) || (sock_gid!=-1)){
if (chown(unixsock_name, sock_uid, sock_gid)<0){
LOG(L_ERR, "ERROR: init_unixsock_socket: failed to change the"
" owner/group for %s to %d.%d; %s[%d]\n",
unixsock_name, sock_uid, sock_gid, strerror(errno), errno);
goto err_rx;
}
}
|
037024d7 |
tx_sock = socket(PF_LOCAL, SOCK_DGRAM, 0);
if (tx_sock == -1) {
|
71fd3ebd |
LOG(L_ERR, "ERROR: init_unixsock_socket: Cannot create TX socket:"
" %s\n", strerror(errno));
|
037024d7 |
goto err_rx;
|
50a0659f |
}
|
037024d7 |
/* Turn non-blocking mode on */
flags = fcntl(tx_sock, F_GETFL);
if (flags == -1){
|
71fd3ebd |
LOG(L_ERR, "ERROR: init_unixsock_socket: fcntl failed: %s\n",
|
037024d7 |
strerror(errno));
goto err_both;
}
if (fcntl(tx_sock, F_SETFL, flags | O_NONBLOCK) == -1) {
|
71fd3ebd |
LOG(L_ERR, "ERROR: init_unixsock_socket: fcntl: "
"set non-blocking failed: %s\n", strerror(errno));
|
037024d7 |
goto err_both;
}
|
b3fef92b |
return 1;
|
037024d7 |
err_both:
close(tx_sock);
err_rx:
close(rx_sock);
return -1;
|
50a0659f |
}
static struct unixsock_cmd* lookup_cmd(str* cmd)
{
struct unixsock_cmd* c;
for(c = cmd_list; c; c = c->next) {
if ((cmd->len == c->name.len) &&
(strncasecmp(c->name.s, cmd->s, cmd->len) == 0)) {
return c;
}
}
return 0;
}
static int parse_cmd(str* res, str* buffer)
{
char* cmd_end;
if (!res || !buffer) {
LOG(L_ERR, "parse_cmd: Invalid parameter value\n");
return -1;
}
if (buffer->len < 3) {
LOG(L_ERR, "parse_cmd: Message too short\n");
return -1;
}
if (buffer->s[0] != CMD_SEPARATOR) {
LOG(L_ERR, "parse_cmd: Command must start with %c\n",
CMD_SEPARATOR);
return -1;
}
cmd_end = q_memchr(buffer->s + 1, CMD_SEPARATOR, buffer->len - 1);
if (!cmd_end) {
LOG(L_ERR, "parse_cmd: Closing '%c' missing\n", CMD_SEPARATOR);
return -1;
}
res->s = buffer->s + 1;
res->len = cmd_end - res->s;
return 0;
}
static void skip_line(str* buffer)
{
if (!buffer) return;
/* Find \n */
while (buffer->len && (buffer->s[0] != '\n')) {
buffer->s++;
buffer->len--;
}
if (buffer->len) {
buffer->s++;
buffer->len--;
}
/* Skip CR following LF */
while (buffer->len && (buffer->s[0] == '\r')) {
buffer->s++;
buffer->len--;
}
}
static void unix_server_loop(void)
{
|
4421b319 |
int ret;
|
50a0659f |
str cmd, buffer;
static char buf[UNIXSOCK_BUF_SIZE];
struct unixsock_cmd* c;
while(1) {
|
4421b319 |
reply_addr_len = sizeof(reply_addr);
|
cf9b82fd |
ret = recvfrom(rx_sock, buf, UNIXSOCK_BUF_SIZE, 0,
|
4421b319 |
(struct sockaddr*)&reply_addr, &reply_addr_len);
|
50a0659f |
if (ret == -1) {
LOG(L_ERR, "unix_server_loop: recvfrom: (%d) %s\n",
errno, strerror(errno));
if ((errno == EINTR) ||
(errno == EAGAIN) ||
(errno == EWOULDBLOCK) ||
(errno == ECONNREFUSED)) {
DBG("unix_server_loop: Got %d (%s), going on\n",
errno, strerror(errno));
continue;
}
|
cf9b82fd |
LOG(L_CRIT, "BUG: unix_server_loop: unexpected recvfrom error\n");
continue;
|
50a0659f |
}
|
cf9b82fd |
buffer.s = buf;
|
50a0659f |
buffer.len = ret;
|
4421b319 |
unixsock_reply_reset();
|
50a0659f |
if (parse_cmd(&cmd, &buffer) < 0) {
|
4421b319 |
unixsock_reply_asciiz("400 First line malformed\n");
unixsock_reply_send();
|
50a0659f |
continue;
}
buffer.s = cmd.s + cmd.len + 1;
buffer.len -= cmd.len + 1 + 1;
skip_line(&buffer); /* Skip the reply filename */
c = lookup_cmd(&cmd);
if (c == 0) {
LOG(L_ERR, "unix_server_loop: Could not find "
"command '%.*s'\n", cmd.len, ZSW(cmd.s));
|
4421b319 |
unixsock_reply_printf("500 Command %.*s not found\n", cmd.len, ZSW(cmd.s));
unixsock_reply_send();
|
50a0659f |
continue;
}
ret = c->f(&buffer);
if (ret < 0) {
LOG(L_ERR, "unix_server_loop: Command '%.*s' failed with "
"return value %d\n", cmd.len, ZSW(cmd.s), ret);
/* Note that we do not send reply here, the
* function is supposed to do so, it knows the
* reason of the failure better than us
*/
}
}
}
|
4421b319 |
static int get_uptime(void)
{
char* t;
time(&up_since);
t = ctime(&up_since);
if (strlen(t) + 1 >= MAX_CTIME_LEN) {
LOG(L_ERR, "get_uptime: Too long date %d\n", (int)strlen(t));
return -1;
}
memcpy(up_since_ctime, t, strlen(t) + 1);
return 0;
}
|
50a0659f |
/*
|
b3fef92b |
* Spawn listeners
|
50a0659f |
*/
|
b3fef92b |
int init_unixsock_children(void)
|
50a0659f |
{
|
b3fef92b |
int i;
|
50a0659f |
pid_t pid;
|
851b529f |
#ifdef USE_TCP
int sockfd[2];
#endif
|
b3fef92b |
if (!unixsock_name || *unixsock_name == '\0') {
|
50a0659f |
return 1;
}
|
4421b319 |
if (get_uptime() < 0) {
return -1;
}
|
b3fef92b |
|
4421b319 |
if (register_core_commands() < 0) {
|
037024d7 |
close(rx_sock);
close(tx_sock);
|
4421b319 |
return -1;
}
|
50a0659f |
for(i = 0; i < unixsock_children; i++) {
process_no++;
|
851b529f |
#ifdef USE_TCP
if(!tcp_disable){
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd)<0){
LOG(L_ERR, "ERROR: init_unixsock_server: socketpair"
" failed: %s\n", strerror(errno));
return -1;
}
}
#endif
|
50a0659f |
pid = fork();
if (pid < 0) {
LOG(L_ERR, "init_unixsock_server: Unable to fork: %s\n",
strerror(errno));
|
037024d7 |
close(rx_sock);
close(tx_sock);
|
50a0659f |
return -1;
} else if (pid == 0) { /* child */
|
851b529f |
#ifdef USE_TCP
if (!tcp_disable){
close(sockfd[0]);
unix_tcp_sock=sockfd[1];
}
#endif
|
50a0659f |
if (init_child(PROC_UNIXSOCK) < 0) {
LOG(L_ERR, "init_unixsock_server: Error in "
"init_child\n");
|
037024d7 |
close(rx_sock);
close(tx_sock);
|
50a0659f |
return -1;
}
unix_server_loop(); /* Never returns */
}
/* Parent */
pt[process_no].pid = pid;
strncpy(pt[process_no].desc, "unix domain socket server",
MAX_PT_DESC);
|
851b529f |
#ifdef USE_TCP
if (!tcp_disable){
close(sockfd[1]);
pt[process_no].unix_sock=sockfd[0];
pt[process_no].idx=-1; /* this is not a "tcp"
process*/
}
#endif
|
50a0659f |
}
|
4421b319 |
|
53c7e0f1 |
DBG("init_unixsock_server: Unix domain socket server successfully initialized @ %s\n",
|
b3fef92b |
unixsock_name);
|
50a0659f |
return 1;
}
/*
* Clean up
*/
void close_unixsock_server(void)
{
struct unixsock_cmd* c;
|
037024d7 |
close(rx_sock);
close(tx_sock);
|
50a0659f |
while(cmd_list) {
c = cmd_list;
cmd_list = cmd_list->next;
pkg_free(c);
}
}
/*
* Register a new command
*/
|
4421b319 |
int unixsock_register_cmd(char* command, unixsock_f* f)
|
50a0659f |
{
|
4421b319 |
str cmd;
|
50a0659f |
struct unixsock_cmd* new_cmd;
|
4421b319 |
cmd.s = command;
cmd.len = strlen(command);
if (lookup_cmd(&cmd)) {
|
50a0659f |
LOG(L_ERR, "unixsock_register_cmd: Function already exists\n");
return -1;
}
new_cmd = pkg_malloc(sizeof(struct unixsock_cmd));
if (new_cmd == 0) {
LOG(L_ERR, "register_unixsock_cmd: Out of mem\n");
return -1;
}
|
4421b319 |
new_cmd->name = cmd;
|
50a0659f |
new_cmd->f = f;
new_cmd->next = cmd_list;
cmd_list = new_cmd;
DBG("unixsock_register_cmd: New command (%.*s) registered\n",
|
4421b319 |
cmd.len, ZSW(cmd.s));
|
50a0659f |
return 1;
}
int unixsock_add_to_reply(const char* buf, size_t len)
{
if (reply_pos.len < len) {
LOG(L_ERR, "unixsock_add_to_reply: Buffer too small\n");
return -1;
}
memcpy(reply_pos.s, buf, len);
reply_pos.s += len;
reply_pos.len -= len;
return 0;
}
/*
* Send a reply
*/
|
4421b319 |
ssize_t unixsock_reply_send(void)
|
50a0659f |
{
|
6b17cae3 |
return tsend_dgram(tx_sock,
reply_buf, reply_pos.s - reply_buf,
(struct sockaddr*)&reply_addr, reply_addr_len,
unixsock_tx_timeout);
|
50a0659f |
}
|
8b3c4193 |
/*
* Send a reply
*/
ssize_t unixsock_reply_sendto(struct sockaddr_un* to)
{
if (!to) {
LOG(L_ERR, "unixsock_reply_sendto: Invalid parameter value\n");
return -1;
}
|
6b17cae3 |
return tsend_dgram(tx_sock,
reply_buf, reply_pos.s - reply_buf,
(struct sockaddr*)to, SUN_LEN(to),
unixsock_tx_timeout);
|
8b3c4193 |
}
|
50a0659f |
/*
* Read a line, the result will be stored in line
* parameter, the data is not copied, it's just
* a pointer to an existing buffer
*/
int unixsock_read_line(str* line, str* source)
{
if (!line || !source) {
LOG(L_ERR, "unixsock_read_line: Invalid parameter value\n");
return -1;
}
*line = *source;
skip_line(source);
line->len = source->s - line->s;
|
372ed8d2 |
trim_trailing(line);
|
50a0659f |
if (line->len) {
return 0;
} else {
return 1;
}
}
/*
* Read body until the closing .CRLF, no CRLF recovery
* is done so no additional buffer is necessary, body will
* point to an existing buffer
*/
int unixsock_read_body(str* body, str* source)
{
int i, state, last_dot;
enum states {
ST_BEGIN,
ST_CRLF,
ST_DATA,
ST_NEWLINE
};
if (!body || !source) {
LOG(L_ERR, "unixsock_read_body: Invalid parameter value\n");
return -1;
}
if (source->len < 2) {
LOG(L_ERR, "unixsock_read_body: Not enough input data "
"(malformed message ?)\n");
return -1;
}
state = ST_BEGIN;
body->s = source->s;
last_dot = 0;
for(i = 0; i < source->len; i++) {
switch(state) {
case ST_BEGIN:
if (source->s[i] == '.') {
last_dot = i;
state = ST_CRLF;
} else if (source->s[i] == '\n') {
state = ST_NEWLINE;
} else {
state = ST_DATA;
}
break;
case ST_CRLF:
if (source->s[i] == '\n') {
body->len = last_dot;
|
7404c5a9 |
source->s += i + 1;
source->len -= i + 1;
|
50a0659f |
return 0;
} else if (source->s[i] != '\r') {
state = ST_DATA;
}
break;
case ST_DATA:
if (source->s[i] == '\n') {
state = ST_NEWLINE;
}
break;
case ST_NEWLINE:
if (source->s[i] == '.') {
last_dot = i;
state = ST_CRLF;
}
break;
}
}
LOG(L_ERR, "unixsock_read_body: Could not find the end of the body\n");
return -1;
}
/*
* Read a set of lines, the functions performs CRLF recovery,
* therefore lineset must point to an additional buffer
* to which the data will be copied. Initial lineset->len contains
* the size of the buffer
*/
int unixsock_read_lineset(str* lineset, str* source)
{
int i, state, len;
enum states {
ST_BEGIN,
ST_CRLF,
ST_DATA,
ST_NEWLINE
};
if (!lineset || !source) {
LOG(L_ERR, "unixsock_read_lineset: Invalid parameter value\n");
return -1;
}
|
86e89f0e |
if (!lineset->s || !lineset->len) {
LOG(L_ERR, "unixsock_read_lineset: Buffer too small\n");
return -1;
}
|
50a0659f |
if (source->len < 2) {
LOG(L_ERR, "unixsock_read_lineset: Not enough input "
"data (malformed message ?)\n");
return -1;
}
state = ST_BEGIN;
len = 0;
for(i = 0; i < source->len; i++) {
if (source->s[i] == '\r') {
/* Filter out CR */
continue;
}
switch(state) {
case ST_BEGIN:
if (source->s[i] == '.') {
state = ST_CRLF;
} else if (source->s[i] == '\n') {
|
86e89f0e |
if (len + 2 > lineset->len) goto buf_err;
|
50a0659f |
lineset->s[len++] = '\r';
lineset->s[len++] = '\n';
state = ST_NEWLINE;
} else {
|
86e89f0e |
if (len + 1 > lineset->len) goto buf_err;
|
50a0659f |
lineset->s[len++] = source->s[i];
}
break;
case ST_CRLF:
if (source->s[i] == '\n') {
lineset->len = len;
|
7404c5a9 |
source->s += i + 1;
source->len -= i + 1;
|
50a0659f |
return 0;
} else {
|
86e89f0e |
if (len + 2 > lineset->len) goto buf_err;
|
50a0659f |
lineset->s[len++] = '.';
lineset->s[len++] = source->s[i];
state = ST_DATA;
}
break;
case ST_DATA:
if (source->s[i] == '\n') {
|
86e89f0e |
if (len + 2 > lineset->len) goto buf_err;
|
50a0659f |
lineset->s[len++] = '\r';
lineset->s[len++] = '\n';
state = ST_NEWLINE;
} else {
|
86e89f0e |
if (len + 1 > lineset->len) goto buf_err;
|
50a0659f |
lineset->s[len++] = source->s[i];
}
break;
case ST_NEWLINE:
if (source->s[i] == '.') {
state = ST_CRLF;
} else {
|
86e89f0e |
if (len + 1 > lineset->len) goto buf_err;
|
50a0659f |
lineset->s[len++] = source->s[i];
state = ST_DATA;
}
break;
}
}
LOG(L_ERR, "unixsock_read_body: Could not find the end of the body\n");
return -1;
|
86e89f0e |
buf_err:
LOG(L_ERR, "unixsock_read_lineset: Buffer too small\n");
return -1;
|
50a0659f |
}
|
4421b319 |
/*
* Reset the reply buffer -- start to write
* at the beginning
*/
void unixsock_reply_reset(void)
{
reply_pos.s = reply_buf;
reply_pos.len = UNIXSOCK_BUF_SIZE;
}
/*
* Add ASCIIZ to the reply buffer
*/
int unixsock_reply_asciiz(char* str)
{
int len;
if (!str) {
LOG(L_ERR, "unixsock_reply_asciiz: Invalid parameter value\n");
return -1;
}
len = strlen(str);
if (reply_pos.len < len) {
LOG(L_ERR, "unixsock_reply_asciiz: Buffer too small\n");
return -1;
}
memcpy(reply_pos.s, str, len);
reply_pos.s += len;
reply_pos.len -= len;
return 0;
}
/*
* Add a string represented by str structure
* to the reply buffer
*/
int unixsock_reply_str(str* s)
{
if (!s) {
LOG(L_ERR, "unixsock_reply_str: Invalid parameter value\n");
return -1;
}
if (reply_pos.len < s->len) {
LOG(L_ERR, "unixsock_reply_str: Buffer too small\n");
return -1;
}
memcpy(reply_pos.s, s->s, s->len);
reply_pos.s += s->len;
reply_pos.len -= s->len;
return 0;
}
/*
* Printf-like reply function
*/
int unixsock_reply_printf(char* fmt, ...)
{
va_list ap;
int ret;
if (!fmt) {
LOG(L_ERR, "unixsock_reply_printf: Invalid parameter value\n");
return -1;
}
va_start(ap, fmt);
ret = vsnprintf(reply_pos.s, reply_pos.len, fmt, ap);
|
e8d77b2a |
if ((ret < 0) || (ret >= reply_pos.len)) {
|
4421b319 |
LOG(L_ERR, "unixsock_reply_printf: Buffer too small\n");
|
e8d77b2a |
va_end(ap);
|
4421b319 |
return -1;
}
va_end(ap);
reply_pos.s += ret;
reply_pos.len -= ret;
return 0;
}
|
8b3c4193 |
/*
* Return the address of the sender
*/
|
7404c5a9 |
struct sockaddr_un* unixsock_sender_addr(void)
|
8b3c4193 |
{
return &reply_addr;
}
|