fifo_server.c
caf80ae6
 /*
  * $Id$
  *
7dd0b342
  *
53c7e0f1
  * Copyright (C) 2001-2003 FhG Fokus
7dd0b342
  *
  * 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
  *
c430ed7b
  * Fifo server is a very powerful tool used to access easily
  * ser's internals via textual interface, similarly to
  * how internals of many operating systems are accessible
  * via the proc file system. This might be used for
  * making ser do things for you (such as initiating new
  * transaction from webpages) or inspect server's health.
  * 
  * FIFO server allows new functionality to be registered
  * with it -- thats what register_fifo_cmd is good for.
  * Remember, the initialization must take place before
  * forking; best in init_module functions. When a function
  * is registered, it can be always evoked by sending its
  * name prefixed by colon to the FIFO.
  *
  * There are few commands already implemented in core.
  * These are 'uptime' for looking at how long the server
  * is alive and 'print' for debugging purposes.
  *
  * Every command sent to FIFO must be sent atomically to
  * avoid intermixing with other commands and MUST be
  * terminated by empty line so that the server is to able
  * to find its end if it does not understand the command.
  *
  * File test/transaction.fifo illustrates example of use
  * of t_uac command (part of TM module).
049f64c2
  *
  * History:
  * --------
3c8bd369
  *  2003-03-29  destroy pkg mem introduced (jiri)
e3dccdc9
  *  2003-03-19  replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei)
3c8bd369
  *  2003-01-29  new built-in fifo commands: arg and pwd (jiri)
58481278
  *  2003-10-07  fifo security fixes: permissions, always delete old fifo,
  *               reply fifo checks -- added fifo_check (andrei)
53c7e0f1
  *  2003-10-13  added fifo_dir for reply fifos (andrei)
cfe4eb4f
  *  2003-10-30  DB interface exported via FIFO (bogdan)
136e7478
  *  2004-03-09  open_fifo_server split into init_ and start_ (andrei)
71fd3ebd
  *  2004-04-29  added chown(sock_user, sock_group)  (andrei)
e3971365
  *  2004-06-06  updated to the new DB interface  & init_db_fifo (andrei)
dd0e65a8
  *  2004-09-19  fifo is deleted on exit (destroy_fifo)  (andrei)
caf80ae6
  */
 
7dd0b342
 
049f64c2
 #include <limits.h>
caf80ae6
 #include <stdlib.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <errno.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <signal.h>
 #include <string.h>
90b0b10d
 #include <time.h>
1c507b72
 #include <stdarg.h>
b988daef
 #ifdef USE_TCP
 #include <sys/socket.h>
 #endif
 
caf80ae6
 #include "dprint.h"
 #include "ut.h"
 #include "error.h"
 #include "config.h"
 #include "globals.h"
 #include "fifo_server.h"
 #include "mem/mem.h"
43d0c484
 #include "sr_module.h"
f51155cf
 #include "pt.h"
5fdbca37
 #include "db/db_fifo.h"
caf80ae6
 
 /* FIFO server vars */
97f17e60
 char *fifo=0; /* FIFO name */
e5f4cdaf
 char* fifo_dir=DEFAULT_FIFO_DIR; /* dir where reply fifos are allowed */
5fdbca37
 char *fifo_db_url = 0;
caf80ae6
 pid_t fifo_pid;
e3971365
 
 
caf80ae6
 /* file descriptors */
 static int fifo_read=0;
 static int fifo_write=0;
136e7478
 static FILE *fifo_stream=0;
caf80ae6
 
 /* list of fifo command */
 static struct fifo_command *cmd_list=0;
 
90b0b10d
 /* up time */
 static time_t up_since;
23348f83
 static char up_since_ctime[MAX_CTIME_LEN];
90b0b10d
 
caf80ae6
 static struct fifo_command *lookup_fifo_cmd( char *name )
 {
 	struct fifo_command *c;
 	for(c=cmd_list; c; c=c->next) {
 		if (strcasecmp(c->name, name)==0) return c;
 	}
 	return 0;
 }
 
 int register_fifo_cmd(fifo_cmd f, char *cmd_name, void *param)
 {
 	struct fifo_command *new_cmd;
 
 	if (lookup_fifo_cmd(cmd_name)) {
 		LOG(L_ERR, "ERROR: register_fifo_cmd: attempt to register synonyms\n");
 		return E_BUG;
 	}
e3dccdc9
 	new_cmd=pkg_malloc(sizeof(struct fifo_command));
caf80ae6
 	if (new_cmd==0) {
 		LOG(L_ERR, "ERROR: register_fifo_cmd: out of mem\n");
 		return E_OUT_OF_MEM;
 	}
 	new_cmd->f=f;
 	new_cmd->name=cmd_name;
 	new_cmd->param=param;
 
 	new_cmd->next=cmd_list;
 	cmd_list=new_cmd;
d95321e4
 	
 	DBG("DEBUG: register_fifo_cmd: new command (%s) registered\n", cmd_name );
caf80ae6
 
 	return 1;
 }
 
3c8bd369
 void destroy_fifo()
 {
 	struct fifo_command *c, *foo;
 
 	c=cmd_list;
 
 	while(c) {
 		foo=c->next;
 		pkg_free(c);
 		c=foo;
 	}
dd0e65a8
 	if (fifo_stream){
 			fclose(fifo_stream);
 			fifo_stream=0;
 		/* if  FIFO was created, delete it */
 		if (fifo && strlen(fifo)){
 			if (unlink(fifo)<0){
 				LOG(L_ERR, "WARNING: destroy_fifo: cannot delete fifo (%s):"
 							" %s\n", fifo, strerror(errno));
 			}
 		}
 	}
3c8bd369
 }
 
caf80ae6
 
 int read_line( char *b, int max, FILE *stream, int *read )
 {
 	int len;
3b8fb572
 	int retry_cnt;
 
 	retry_cnt=0;
 
 retry:
caf80ae6
 	if (fgets(b, max, stream)==NULL) {
 		LOG(L_ERR, "ERROR: fifo_server fgets failed: %s\n",
 			strerror(errno));
3b8fb572
 		/* on Linux, fgets sometimes returns ESPIPE -- give
 		   it few more chances
 		*/
 		if (errno==ESPIPE) {
 			retry_cnt++;
 			if (retry_cnt<4) goto retry;
 		}
d615a5cd
 		/* interrupted by signal or ... */
 		if ((errno==EINTR)||(errno==EAGAIN)) goto retry;
caf80ae6
 		kill(0, SIGTERM);
 	}
 	/* if we did not read whole line, our buffer is too small
 	   and we cannot process the request; consume the remainder of 
 	   request
 	*/
 	len=strlen(b);
 	if (len && !(b[len-1]=='\n' || b[len-1]=='\r')) {
 		LOG(L_ERR, "ERROR: read_line: request  line too long\n");
 		return 0;
 	}
 	/* trim from right */
 	while(len) {
 		if(b[len-1]=='\n' || b[len-1]=='\r'
 				|| b[len-1]==' ' || b[len-1]=='\t' ) {
 			len--;
 			b[len]=0;
 		} else break;
 	}
 	*read=len;
 	return 1;
 }
 
 static void consume_request( FILE *stream )
 {
 	int len;
 	char buffer[MAX_CONSUME_BUFFER];
 
 	while(!read_line(buffer, MAX_CONSUME_BUFFER, stream, &len));
 
 }
 
 int read_eol( FILE *stream )
 {
 	int len;
 	char buffer[MAX_CONSUME_BUFFER];
 	if (!read_line(buffer, MAX_CONSUME_BUFFER, stream, &len) || len!=0) {
 		LOG(L_ERR, "ERROR: read_eol: EOL expected: %.10s...\n",
 			buffer );
 		return 0;
 	}
 	return 1;
 }
c430ed7b
 
 /* read from input until empty line is encountered */	
caf80ae6
 int read_line_set(char *buf, int max_len, FILE *fifo, int *len)
 {
 	int set_len;
 	char *c;
 	int line_len;
 
 	c=buf;set_len=0;
 	while(1) {
 		if (!read_line(c,max_len,fifo,&line_len)) {
 			LOG(L_ERR, "ERROR: fifo_server: line expected\n");
 			return 0;
 		}
 		/* end encountered ... return */
ff979952
 		if (line_len==0 || (line_len==1 && c[0]=='.' )) {
caf80ae6
 			*len=set_len;
 			return 1;
 		}
 		max_len-=line_len; c+=line_len; set_len+=line_len;
 		if (max_len<CRLF_LEN) {
 			LOG(L_ERR, "ERROR: fifo_server: no place for CRLF\n");
 			return 0;
 		}
 		memcpy(c, CRLF, CRLF_LEN);
 		max_len-=CRLF_LEN; c+=CRLF_LEN; set_len+=CRLF_LEN;
 	}
 }
 
ff979952
 
c430ed7b
 /* read from input until line with only dot in it is encountered */
 int read_body(char *buf, int max_len, FILE *fifo, int *len)
 {
 	int set_len;
 	char *c;
 	int line_len;
 
 	c=buf;set_len=0;
 	while(1) {
 		if (!read_line(c,max_len,fifo,&line_len)) {
 			LOG(L_ERR, "ERROR: fifo_server: line expected\n");
 			return 0;
 		}
 		/* end encountered ... return */
 		if (line_len==1 && *c=='.') {
 			*len=set_len;
 			return 1;
 		}
 		max_len-=line_len; c+=line_len; set_len+=line_len;
 		if (max_len<CRLF_LEN) {
 			LOG(L_ERR, "ERROR: fifo_server: no place for CRLF\n");
 			return 0;
 		}
 		memcpy(c, CRLF, CRLF_LEN);
 		max_len-=CRLF_LEN; c+=CRLF_LEN; set_len+=CRLF_LEN;
 	}
 }
 
caf80ae6
 static char *trim_filename( char * file )
 {
 	int prefix_len, fn_len;
 	char *new_fn;
 
 	/* we only allow files in "/tmp" -- any directory
 	   changes are not welcome
 	*/
 	if (strchr(file,'.') || strchr(file,'/')
 				|| strchr(file, '\\')) {
 		LOG(L_ERR, "ERROR: trim_filename: forbidden filename: %s\n"
 			, file);
 		return 0;
 	}
e5f4cdaf
 	prefix_len=strlen(fifo_dir); fn_len=strlen(file);
caf80ae6
 	new_fn=pkg_malloc(prefix_len+fn_len+1);
 	if (new_fn==0) {
 		LOG(L_ERR, "ERROR: trim_filename: no mem\n");
 		return 0;
 	}
 
e5f4cdaf
 	memcpy(new_fn, fifo_dir, prefix_len);
caf80ae6
 	memcpy(new_fn+prefix_len, file, fn_len );
 	new_fn[prefix_len+fn_len]=0;
 
 	return new_fn;
 }
 
58481278
 
 
 /* reply fifo security checks:
  * checks if fd is a fifo, is not hardlinked and it's not a softlink
  * opened file descriptor + file name (for soft link check)
  * returns 0 if ok, <0 if not */
 static int fifo_check(int fd, char* fname)
 {
 	struct stat fst;
 	struct stat lst;
 	
 	if (fstat(fd, &fst)<0){
 		LOG(L_ERR, "ERROR: fifo_check: fstat failed: %s\n",
 				strerror(errno));
 		return -1;
 	}
 	/* check if fifo */
 	if (!S_ISFIFO(fst.st_mode)){
 		LOG(L_ERR, "ERROR: fifo_check: %s is not a fifo\n", fname);
 		return -1;
 	}
 	/* check if hard-linked */
 	if (fst.st_nlink>1){
 		LOG(L_ERR, "ERROR: security: fifo_check: %s is hard-linked %d times\n",
4d080f49
 				fname, (unsigned)fst.st_nlink);
58481278
 		return -1;
 	}
 	
 	/* lstat to check for soft links */
 	if (lstat(fname, &lst)<0){
 		LOG(L_ERR, "ERROR: fifo_check: lstat failed: %s\n",
 				strerror(errno));
 		return -1;
 	}
 	if (S_ISLNK(lst.st_mode)){
 		LOG(L_ERR, "ERROR: security: fifo_check: %s is a soft link\n",
 				fname);
 		return -1;
 	}
 	/* if this is not a symbolic link, check to see if the inode didn't
 	 * change to avoid possible sym.link, rm sym.link & replace w/ fifo race
 	 */
 	if ((lst.st_dev!=fst.st_dev)||(lst.st_ino!=fst.st_ino)){
 		LOG(L_ERR, "ERROR: security: fifo_check: inode/dev number differ"
06aaa54f
 				": %d %d (%s)\n",
 				 (int)fst.st_ino, (int)lst.st_ino, fname);
58481278
 		return -1;
 	}
 	/* success */
 	return 0;
 }
 
 
 
4e8314bf
 /* tell FIFO client what happened via reply pipe */
1c507b72
 void fifo_reply( char *reply_fifo, char *reply_fmt, ... )
4e8314bf
 {
 	FILE *file_handle;
1c507b72
 	int r;
 	va_list ap;
4e8314bf
 
 	file_handle=open_reply_pipe(reply_fifo);
 	if (file_handle==0) {
 		LOG(L_ERR, "ERROR: fifo_reply: no reply pipe %s\n",
a50d55d3
 			reply_fifo);
4e8314bf
 		return;
 	}
1c507b72
 retry:
 	va_start(ap, reply_fmt);
 	r=vfprintf(file_handle, reply_fmt, ap);
 	va_end(ap);
 	if (r<=0) {
4e8314bf
 		LOG(L_ERR, "ERROR: fifo_error: write error (%s): %s\n",
 			fifo, strerror(errno));
1c507b72
 		if ((errno==EINTR)||(errno==EAGAIN)||(errno==EWOULDBLOCK)) {
 			goto retry;
 		}
4e8314bf
 	}
 	fclose(file_handle);
 }
 
90b0b10d
 FILE *open_reply_pipe( char *pipe_name )
 {
1c507b72
 
 	int fifofd;
90b0b10d
 	FILE *file_handle;
88962a00
 	int flags;
90b0b10d
 
1c507b72
 	int retries=FIFO_REPLY_RETRIES;
 
 	if (!pipe_name || *pipe_name==0) {
90b0b10d
 		DBG("DEBUG: open_reply_pipe: no file to write to about missing cmd\n");
 		return 0;
 	}
1c507b72
 
 tryagain:
88962a00
 	/* open non-blocking to make sure that a broken client will not 
 	 * block the FIFO server forever */
1c507b72
 	fifofd=open( pipe_name, O_WRONLY | O_NONBLOCK );
 	if (fifofd==-1) {
 		/* retry several times if client is not yet ready for getting
 		   feedback via a reply pipe
 		*/
 		if (errno==ENXIO) {
 			/* give up on the client - we can't afford server blocking */
 			if (retries==0) {
 				LOG(L_ERR, "ERROR: open_reply_pipe: no client at %s\n",
 					pipe_name );
 				return 0;
 			}
 			/* don't be noisy on the very first try */
 			if (retries!=FIFO_REPLY_RETRIES)
 				DBG("DEBUG: open_reply_pipe: retry countdown: %d\n", retries );
 			sleep_us( FIFO_REPLY_WAIT );
 			retries--;
 			goto tryagain;
 		}
 		/* some other opening error */
 		LOG(L_ERR, "ERROR: open_reply_pipe: open error (%s): %s\n",
 			pipe_name, strerror(errno));
 		return 0;
 	}
58481278
 	/* security checks: is this really a fifo?, is 
 	 * it hardlinked? is it a soft link? */
 	if (fifo_check(fifofd, pipe_name)<0) goto error;
 	
88962a00
 	/* we want server blocking for big writes */
 	if ( (flags=fcntl(fifofd, F_GETFL, 0))<0) {
 		LOG(L_ERR, "ERROR: open_reply_pipe (%s): getfl failed: %s\n",
 			pipe_name, strerror(errno));
58c780f0
 		goto error;
88962a00
 	}
 	flags&=~O_NONBLOCK;
 	if (fcntl(fifofd, F_SETFL, flags)<0) {
 		LOG(L_ERR, "ERROR: open_reply_pipe (%s): setfl cntl failed: %s\n",
 			pipe_name, strerror(errno));
58c780f0
 		goto error;
88962a00
 	}
 
 	/* create an I/O stream */	
1c507b72
 	file_handle=fdopen( fifofd, "w");
90b0b10d
 	if (file_handle==NULL) {
 		LOG(L_ERR, "ERROR: open_reply_pipe: open error (%s): %s\n",
 			pipe_name, strerror(errno));
58c780f0
 		goto error;
90b0b10d
 	}
 	return file_handle;
58c780f0
 error:
 	close(fifofd);
 	return 0;
90b0b10d
 }
 
caf80ae6
 static void fifo_server(FILE *fifo_stream)
 {
 	char buf[MAX_FIFO_COMMAND];
 	int line_len;
 	char *file_sep, *command, *file;
 	struct fifo_command *f;
 
 	file_sep=command=file=0;
 
5fdbca37
 	/* register a diagnostic FIFO command */
 	register_core_fifo();
 
caf80ae6
 	while(1) {
 
 		/* commands must look this way ':<command>:[filename]' */
 		if (!read_line(buf, MAX_FIFO_COMMAND, fifo_stream, &line_len)) {
 			/* line breaking must have failed -- consume the rest
 			   and proceed to a new request
 			*/
 			LOG(L_ERR, "ERROR: fifo_server: command expected\n");
 			goto consume;
 		}
 		if (line_len==0) {
e657aa3e
 			LOG(L_DBG, "INFO: fifo_server: command empty\n");
caf80ae6
 			continue;
 		}
 		if (line_len<3) {
 			LOG(L_ERR, "ERROR: fifo_server: command must have at least 3 chars\n");
 			goto consume;
 		}
 		if (*buf!=CMD_SEPARATOR) {
1380e79e
 			LOG(L_ERR, "ERROR: fifo_server: command must begin with %c: %.*s\n", 
 				CMD_SEPARATOR, line_len, buf );
caf80ae6
 			goto consume;
 		}
 		command=buf+1;
 		file_sep=strchr(command, CMD_SEPARATOR );
 		if (file_sep==NULL) {
 			LOG(L_ERR, "ERROR: fifo_server: file separator missing\n");
 			goto consume;
 		}
 		if (file_sep==command) {
 			LOG(L_ERR, "ERROR: fifo_server: empty command\n");
 			goto consume;
 		}
 		if (*(file_sep+1)==0) file=NULL; 
 		else {
 			file=file_sep+1;
 			file=trim_filename(file);
 			if (file==0) {
 				LOG(L_ERR, "ERROR: fifo_server: trimming filename\n");
 				goto consume;
 			}
 		}
 		/* make command zero-terminated */
 		*file_sep=0;
 
 		f=lookup_fifo_cmd( command );
 		if (f==0) {
 			LOG(L_ERR, "ERROR: fifo_server: command %s is not available\n",
 				command);
249cc934
 			fifo_reply(file, "500 command '%s' not available\n", command);
caf80ae6
 			goto consume;
 		}
 		if (f->f(fifo_stream, file)<0) {
 			LOG(L_ERR, "ERROR: fifo_server: command (%s) "
 				"processing failed\n", command );
 			goto consume;
 		}
 
 consume:
 		if (file) { pkg_free(file); file=0;}
 		consume_request(fifo_stream);
5fdbca37
 		DBG("**** done consume\n");
caf80ae6
 	}
 }
 
136e7478
 int init_fifo_server()
caf80ae6
 {
23348f83
 	char *t;
73eaebd2
 	struct stat filestat;
74fae827
 	int n;
136e7478
 	long opt;
23348f83
 
caf80ae6
 	if (fifo==NULL) {
e1f5d39f
 		DBG("DBG: open_fifo_server: no fifo will be opened\n");
caf80ae6
 		/* everything is ok, we just do not want to start */
 		return 1;
 	}
606ba517
 	if (strlen(fifo)==0) {
e1f5d39f
 		DBG("DBG: open_fifo_server: fifo disabled\n");
606ba517
 		return 1;
 	}
e1f5d39f
 	DBG("DBG: open_uac_fifo: opening fifo...\n");
74fae827
 	n=stat(fifo, &filestat);
 	if (n==0){
58481278
 		/* FIFO exist, delete it (safer) */
 		if (unlink(fifo)<0){
 			LOG(L_ERR, "ERROR: open_fifo_server: cannot delete old fifo (%s):"
 					" %s\n", fifo, strerror(errno));
 			return -1;
 		}
74fae827
 	}else if (n<0 && errno!=ENOENT){
73eaebd2
 		LOG(L_DBG, "DEBUG: open_fifo_server: FIFO stat failed: %s\n",
 			strerror(errno));
74fae827
 	}
 		/* create FIFO ... */
71fd3ebd
 		if ((mkfifo(fifo, sock_mode)<0)) {
73eaebd2
 			LOG(L_ERR, "ERROR: open_fifo_server; can't create FIFO: "
 					"%s (mode=%d)\n",
71fd3ebd
 					strerror(errno), sock_mode);
73eaebd2
 			return -1;
 		} 
 		DBG("DEBUG: FIFO created @ %s\n", fifo );
71fd3ebd
 		if ((chmod(fifo, sock_mode)<0)) {
73eaebd2
 			LOG(L_ERR, "ERROR: open_fifo_server; can't chmod FIFO: "
 					"%s (mode=%d)\n",
71fd3ebd
 					strerror(errno), sock_mode);
73eaebd2
 			return -1;
 		}
71fd3ebd
 		if ((sock_uid!=-1) || (sock_gid!=-1)){
 			if (chown(fifo, sock_uid, sock_gid)<0){
 			LOG(L_ERR, "ERROR: open_fifo_server: failed to change the"
 					" owner/group for %s  to %d.%d; %s[%d]\n",
 					fifo, sock_uid, sock_gid, strerror(errno), errno);
 			return -1;
 		}
 	}
 
 		
 	DBG("DEBUG: fifo %s opened, mode=%d\n", fifo, sock_mode );
90b0b10d
 	time(&up_since);
23348f83
 	t=ctime(&up_since);
 	if (strlen(t)+1>=MAX_CTIME_LEN) {
 		LOG(L_ERR, "ERROR: open_fifo_server: "
9a694681
 			"too long date %d\n", (int)strlen(t));
23348f83
 		return -1;
 	}
 	memcpy(up_since_ctime,t,strlen(t)+1);
136e7478
 	/* open it non-blocking or else wait here until someone
53c7e0f1
 	 * opens it for writing */
136e7478
 	fifo_read=open(fifo, O_RDONLY|O_NONBLOCK, 0);
 	if (fifo_read<0) {
 		LOG(L_ERR, "ERROR: init_fifo_server: fifo_read did not open: %s\n",
 			strerror(errno));
 		return -1;
 	}
 	fifo_stream=fdopen(fifo_read, "r");
 	if (fifo_stream==NULL) {
 		LOG(L_ERR, "ERROR: init_fifo_server: fdopen failed: %s\n",
 			strerror(errno));
 		return -1;
 	}
 	/* make sure the read fifo will not close */
 	fifo_write=open(fifo, O_WRONLY|O_NONBLOCK, 0);
 	if (fifo_write<0) {
 		LOG(L_ERR, "ERROR: init_fifo_server: fifo_write did not open: %s\n",
 			strerror(errno));
 		return -1;
 	}
 	/* set read fifo blocking mode */
 	if ((opt=fcntl(fifo_read, F_GETFL))==-1){
 		LOG(L_ERR, "ERROR: init_fifo_server: fcntl(F_GETFL) failed: %s [%d]\n",
 				strerror(errno), errno);
 		return -1;
 	}
 	if (fcntl(fifo_read, F_SETFL, opt & (~O_NONBLOCK))==-1){
 		LOG(L_ERR, "ERROR: init_fifo_server: fcntl(F_SETFL) failed: %s [%d]\n",
 				strerror(errno), errno);
 		return -1;
 	}
 	return 0;
 }
 
 
 
 int start_fifo_server()
 {
 #ifdef USE_TCP
 	int sockfd[2];
 #endif
 	if (fifo_stream==0) return 1; /* no error, we just don't start it */
0c5da34b
 #ifdef USE_TCP
b988daef
 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd)<0){
0c5da34b
 			LOG(L_ERR, "ERROR: open_fifo_server: socketpair failed: %s\n",
 				strerror(errno));
 			return -1;
 	}
 #endif
caf80ae6
 	process_no++;
 	fifo_pid=fork();
 	if (fifo_pid<0) {
 		LOG(L_ERR, "ERROR: open_fifo_server: failure to fork: %s\n",
 			strerror(errno));
 		return -1;
 	}
 	if (fifo_pid==0) { /* child == FIFO server */
 		LOG(L_INFO, "INFO: fifo process starting: %d\n", getpid());
1380e79e
 		/* call per-child module initialization too -- some
 		   FIFO commands may need it
 		*/
0c5da34b
 #ifdef USE_TCP
 		close(sockfd[0]);
 		unix_tcp_sock=sockfd[1];
 #endif
cb87691a
 		if (init_child(PROC_FIFO) < 0 ) {
1380e79e
 			LOG(L_ERR, "ERROR: open_uac_fifo: init_child failed\n");
 			return -1;
 		}
1c507b72
 		/* a real server doesn't die if writing to reply fifo fails */
 		signal(SIGPIPE, SIG_IGN);
f1d96242
 		LOG(L_INFO, "SER: open_uac_fifo: fifo server up at %s...\n",
 			fifo);
53c7e0f1
 		fifo_server( fifo_stream ); /* never returns */
caf80ae6
 	}
 	/* dad process */
f51155cf
 	pt[process_no].pid=fifo_pid;
 	strncpy(pt[process_no].desc, "fifo server", MAX_PT_DESC );
0c5da34b
 #ifdef USE_TCP
 	close(sockfd[1]);
 	pt[process_no].unix_sock=sockfd[0];
 	pt[process_no].idx=-1; /* this is not "tcp" process*/
 #endif
caf80ae6
 	return 1;
 }
 
3165311a
 static int print_version_cmd( FILE *stream, char *response_file )
 {
 	if (response_file) {
249cc934
 		fifo_reply(response_file, "200 ok\n" SERVER_HDR CRLF );
3165311a
 	} else {
049f64c2
 		LOG(L_ERR, "ERROR: no file for %s\n", "print_version_cmd" );
 	}
 	return 1;
 }
 
 static int pwd_cmd( FILE *stream, char *response_file )
 {
 	char *cwd_buf;
 	int max_len;
 
 	if (!response_file) {
 		LOG(L_ERR, "ERROR: no file for %s\n", "pwd_cmd" );
 		return 1;
 	}
 
 	max_len=pathmax();
 	cwd_buf=pkg_malloc(max_len);
 	if (!cwd_buf) {
 		LOG(L_ERR, "ERROR: pwd_cmd: no cwd pkg mem\n");
 		fifo_reply(response_file, "500 no memory\n");
 		return 1;
 	}
 
 	if (getcwd(cwd_buf, max_len)) {
 		fifo_reply(response_file, "200 ok\n%s\n", cwd_buf );
 	} else {
 		fifo_reply(response_file, "500 getcwd failed\n" );
 	}
 
 	pkg_free(cwd_buf);
 	return 1;
 }
 
 static int arg_cmd( FILE *stream, char *response_file )
 {
 	FILE *reply_pipe;
 	int p;
 
 	if (response_file==0 || *response_file==0 ) {
 		 LOG(L_ERR, "ERROR: ps_fifo_cmd: null file\n");
 		return -1;
 	}
 	reply_pipe=open_reply_pipe(response_file);
 	if (reply_pipe==NULL) {
 		LOG(L_ERR, "ERROR: ps_fifo_cmd: opening reply pipe (%s) failed\n",
 			response_file );
 		return -1;
3165311a
 	}
049f64c2
 
 	fputs( "200 ok\n", reply_pipe);
 	for (p=0; p<my_argc;p++) 
 			fprintf( reply_pipe, "%s\n", my_argv[p] );
 			
 	fclose(reply_pipe);
3165311a
 	return 1;
 }
049f64c2
 
3165311a
 	
 
caf80ae6
 /* diagnostic and hello-world FIFO command */
90b0b10d
 static int print_fifo_cmd( FILE *stream, char *response_file )
caf80ae6
 {
 	char text[MAX_PRINT_TEXT];
 	int text_len;
 	
 	/* expect one line which will be printed out */
4e8314bf
 	if (response_file==0 || *response_file==0 ) { 
 		LOG(L_ERR, "ERROR: print_fifo_cmd: null file\n");
 		return -1;
 	}
caf80ae6
 	if (!read_line(text, MAX_PRINT_TEXT, stream, &text_len)) {
4e8314bf
 		fifo_reply(response_file, 
249cc934
 			"500 print_fifo_cmd: too big text");
caf80ae6
 		return -1;
 	}
 	/* now the work begins */
 	if (response_file) {
249cc934
 		fifo_reply(response_file, "200 ok\n%s\n", text );
caf80ae6
 	} else {
 		LOG(L_INFO, "INFO: print_fifo_cmd: %.*s\n", 
 			text_len, text );
 	}
 	return 1;
 }
90b0b10d
 
 static int uptime_fifo_cmd( FILE *stream, char *response_file )
 {
 	time_t now;
 
 	if (response_file==0 || *response_file==0 ) { 
 		LOG(L_ERR, "ERROR: uptime_fifo_cmd: null file\n");
 		return -1;
 	}
1c507b72
 
 	time(&now);
249cc934
 	fifo_reply( response_file, "200 ok\n"
 		"Now: %sUp Since: %sUp time: %.0f [sec]\n",
23348f83
 		ctime(&now), up_since_ctime, difftime(now, up_since) );
1c507b72
 
90b0b10d
 	return 1;
 }
 
95e2fa9d
 static int kill_fifo_cmd( FILE *stream, char *response_file )
 {
 	if (response_file==0 || *response_file==0 ) { 
 		LOG(L_ERR, "ERROR: uptime_fifo_cmd: null file\n");
 		return -1;
 	}
 	fifo_reply(response_file, "200 killing now..." );
 	kill(0, SIGTERM);
 	return 1;
 }
 
114eaa41
 static int which_fifo_cmd(FILE *stream, char *response_file )
 {
 	FILE *reply_pipe;
 	struct fifo_command *c;
 
 	if (response_file==0 || *response_file==0 ) {
 		 LOG(L_ERR, "ERROR: which_fifo_cmd: null file\n");
 		return -1;
 	}
 
 	reply_pipe=open_reply_pipe(response_file);
 	if (reply_pipe==NULL) {
f51155cf
 		LOG(L_ERR, "ERROR: which_fifo_cmd: opening reply pipe (%s) failed\n",
114eaa41
 			response_file );
 		return -1;
 	}
249cc934
 	fputs( "200 ok\n", reply_pipe);
114eaa41
 	for(c=cmd_list; c; c=c->next) {
 		fprintf( reply_pipe, "%s\n", c->name );
 	}
 
 	fclose(reply_pipe);
 	return 1;
 }
 
f51155cf
 static int ps_fifo_cmd(FILE *stream, char *response_file )
 {
 	FILE *reply_pipe;
 	int p;
 
 	if (response_file==0 || *response_file==0 ) {
 		 LOG(L_ERR, "ERROR: ps_fifo_cmd: null file\n");
 		return -1;
 	}
 	reply_pipe=open_reply_pipe(response_file);
 	if (reply_pipe==NULL) {
 		LOG(L_ERR, "ERROR: ps_fifo_cmd: opening reply pipe (%s) failed\n",
 			response_file );
 		return -1;
 	}
 
249cc934
 	fputs( "200 ok\n", reply_pipe);
f51155cf
 	for (p=0; p<process_count();p++) 
 		fprintf( reply_pipe, "%d\t%d\t%s\n",
 			p, pt[p].pid, pt[p].desc );
 
 	fclose(reply_pipe);
 	return 1;
 }
 
90b0b10d
 
 int register_core_fifo()
 {
 	if (register_fifo_cmd(print_fifo_cmd, FIFO_PRINT, 0)<0) {
5fdbca37
 		LOG(L_ERR, "ERROR: unable to register '%s' FIFO cmd\n", FIFO_PRINT);
90b0b10d
 		return -1;
 	}
 	if (register_fifo_cmd(uptime_fifo_cmd, FIFO_UPTIME, 0)<0) {
5fdbca37
 		LOG(L_ERR, "ERROR: unable to register '%s' FIFO cmd\n", FIFO_UPTIME);
90b0b10d
 		return -1;
 	}
3165311a
 	if (register_fifo_cmd(print_version_cmd, FIFO_VERSION, 0)<0) {
5fdbca37
 		LOG(L_ERR, "ERROR: unable to register '%s' FIFO cmd\n",FIFO_VERSION);
3165311a
 		return -1;
 	}
049f64c2
 	if (register_fifo_cmd(pwd_cmd, FIFO_PWD, 0)<0) {
5fdbca37
 		LOG(L_ERR, "ERROR: unable to register '%s' FIFO cmd\n", FIFO_PWD);
049f64c2
 		return -1;
 	}
 	if (register_fifo_cmd(arg_cmd, FIFO_ARG, 0)<0) {
5fdbca37
 		LOG(L_ERR, "ERROR: unable to register '%s' FIFO cmd\n", FIFO_ARG);
049f64c2
 		return -1;
 	}
114eaa41
 	if (register_fifo_cmd(which_fifo_cmd, FIFO_WHICH, 0)<0) {
5fdbca37
 		LOG(L_ERR, "ERROR: unable to register '%s' FIFO cmd\n", FIFO_WHICH);
f51155cf
 		return -1;
 	}
 	if (register_fifo_cmd(ps_fifo_cmd, FIFO_PS, 0)<0) {
5fdbca37
 		LOG(L_ERR, "ERROR: unable to register '%s' FIFO cmd\n", FIFO_PS);
114eaa41
 		return -1;
 	}
95e2fa9d
 	if (register_fifo_cmd(kill_fifo_cmd, FIFO_KILL, 0)<0) {
5fdbca37
 		LOG(L_CRIT, "ERROR: unable to register '%s' FIFO cmd\n", FIFO_KILL);
95e2fa9d
 		return -1;
 	}
5fdbca37
 	if (fifo_db_url==0) {
 		LOG(L_WARN,"WARNING: no fifo_db_url given - "
 			"fifo DB commands disabled!\n");
e3971365
 	} else if (init_db_fifo(fifo_db_url)<0){
 		return -1;
5fdbca37
 	}
90b0b10d
 	return 1;
 }