|
@@ -1,950 +0,0 @@
|
|
|
-/*
|
|
|
- * $Id$
|
|
|
- *
|
|
|
- *
|
|
|
- * Copyright (C) 2001-2003 FhG Fokus
|
|
|
- *
|
|
|
- * 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:
|
|
|
- * [email protected]
|
|
|
- *
|
|
|
- * 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
|
|
|
- *
|
|
|
- * 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).
|
|
|
- *
|
|
|
- * History:
|
|
|
- * --------
|
|
|
- * 2003-03-29 destroy pkg mem introduced (jiri)
|
|
|
- * 2003-03-19 replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei)
|
|
|
- * 2003-01-29 new built-in fifo commands: arg and pwd (jiri)
|
|
|
- * 2003-10-07 fifo security fixes: permissions, always delete old fifo,
|
|
|
- * reply fifo checks -- added fifo_check (andrei)
|
|
|
- * 2003-10-13 added fifo_dir for reply fifos (andrei)
|
|
|
- * 2003-10-30 DB interface exported via FIFO (bogdan)
|
|
|
- * 2004-03-09 open_fifo_server split into init_ and start_ (andrei)
|
|
|
- * 2004-04-29 added chown(sock_user, sock_group) (andrei)
|
|
|
- * 2004-06-06 updated to the new DB interface & init_db_fifo (andrei)
|
|
|
- * 2004-09-19 fifo is deleted on exit (destroy_fifo) (andrei)
|
|
|
- * 2005-03-02 meminfo fifo cmd added (andrei)
|
|
|
- */
|
|
|
-
|
|
|
-
|
|
|
-#include <limits.h>
|
|
|
-#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>
|
|
|
-#include <time.h>
|
|
|
-#include <stdarg.h>
|
|
|
-#ifdef USE_TCP
|
|
|
-#include <sys/socket.h>
|
|
|
-#endif
|
|
|
-
|
|
|
-#include "dprint.h"
|
|
|
-#include "ut.h"
|
|
|
-#include "error.h"
|
|
|
-#include "config.h"
|
|
|
-#include "globals.h"
|
|
|
-#include "fifo_server.h"
|
|
|
-#include "mem/mem.h"
|
|
|
-#include "mem/shm_mem.h" /* shm_info() */
|
|
|
-#include "sr_module.h"
|
|
|
-#include "pt.h"
|
|
|
-#include "db/db_fifo.h"
|
|
|
-
|
|
|
-/* FIFO server vars */
|
|
|
-char *fifo=0; /* FIFO name */
|
|
|
-char* fifo_dir=DEFAULT_FIFO_DIR; /* dir where reply fifos are allowed */
|
|
|
-char *fifo_db_url = 0;
|
|
|
-pid_t fifo_pid;
|
|
|
-
|
|
|
-
|
|
|
-/* file descriptors */
|
|
|
-static int fifo_read=0;
|
|
|
-static int fifo_write=0;
|
|
|
-static FILE *fifo_stream=0;
|
|
|
-
|
|
|
-/* list of fifo command */
|
|
|
-static struct fifo_command *cmd_list=0;
|
|
|
-
|
|
|
-/* up time */
|
|
|
-static time_t up_since;
|
|
|
-static char up_since_ctime[MAX_CTIME_LEN];
|
|
|
-
|
|
|
-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;
|
|
|
- }
|
|
|
- new_cmd=pkg_malloc(sizeof(struct fifo_command));
|
|
|
- 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;
|
|
|
-
|
|
|
- DBG("DEBUG: register_fifo_cmd: new command (%s) registered\n", cmd_name );
|
|
|
-
|
|
|
- return 1;
|
|
|
-}
|
|
|
-
|
|
|
-void destroy_fifo()
|
|
|
-{
|
|
|
- struct fifo_command *c, *foo;
|
|
|
-
|
|
|
- c=cmd_list;
|
|
|
-
|
|
|
- while(c) {
|
|
|
- foo=c->next;
|
|
|
- pkg_free(c);
|
|
|
- c=foo;
|
|
|
- }
|
|
|
- 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));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-int read_line( char *b, int max, FILE *stream, int *read )
|
|
|
-{
|
|
|
- int len;
|
|
|
- int retry_cnt;
|
|
|
-
|
|
|
- retry_cnt=0;
|
|
|
-
|
|
|
-retry:
|
|
|
- if (fgets(b, max, stream)==NULL) {
|
|
|
- LOG(L_ERR, "ERROR: fifo_server fgets failed: %s\n",
|
|
|
- strerror(errno));
|
|
|
- /* on Linux, fgets sometimes returns ESPIPE -- give
|
|
|
- it few more chances
|
|
|
- */
|
|
|
- if (errno==ESPIPE) {
|
|
|
- retry_cnt++;
|
|
|
- if (retry_cnt<4) goto retry;
|
|
|
- }
|
|
|
- /* interrupted by signal or ... */
|
|
|
- if ((errno==EINTR)||(errno==EAGAIN)) goto retry;
|
|
|
- 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;
|
|
|
-}
|
|
|
-
|
|
|
-/* read from input until empty line is encountered */
|
|
|
-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 */
|
|
|
- if (line_len==0 || (line_len==1 && c[0]=='.' )) {
|
|
|
- *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;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/* 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;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-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;
|
|
|
- }
|
|
|
- prefix_len=strlen(fifo_dir); fn_len=strlen(file);
|
|
|
- new_fn=pkg_malloc(prefix_len+fn_len+1);
|
|
|
- if (new_fn==0) {
|
|
|
- LOG(L_ERR, "ERROR: trim_filename: no mem\n");
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- memcpy(new_fn, fifo_dir, prefix_len);
|
|
|
- memcpy(new_fn+prefix_len, file, fn_len );
|
|
|
- new_fn[prefix_len+fn_len]=0;
|
|
|
-
|
|
|
- return new_fn;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-/* 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",
|
|
|
- fname, (unsigned)fst.st_nlink);
|
|
|
- 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"
|
|
|
- ": %d %d (%s)\n",
|
|
|
- (int)fst.st_ino, (int)lst.st_ino, fname);
|
|
|
- return -1;
|
|
|
- }
|
|
|
- /* success */
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-/* tell FIFO client what happened via reply pipe */
|
|
|
-void fifo_reply( char *reply_fifo, char *reply_fmt, ... )
|
|
|
-{
|
|
|
- FILE *file_handle;
|
|
|
- int r;
|
|
|
- va_list ap;
|
|
|
-
|
|
|
- file_handle=open_reply_pipe(reply_fifo);
|
|
|
- if (file_handle==0) {
|
|
|
- LOG(L_ERR, "ERROR: fifo_reply: no reply pipe %s\n",
|
|
|
- reply_fifo);
|
|
|
- return;
|
|
|
- }
|
|
|
-retry:
|
|
|
- va_start(ap, reply_fmt);
|
|
|
- r=vfprintf(file_handle, reply_fmt, ap);
|
|
|
- va_end(ap);
|
|
|
- if (r<=0) {
|
|
|
- LOG(L_ERR, "ERROR: fifo_error: write error (%s): %s\n",
|
|
|
- fifo, strerror(errno));
|
|
|
- if ((errno==EINTR)||(errno==EAGAIN)||(errno==EWOULDBLOCK)) {
|
|
|
- goto retry;
|
|
|
- }
|
|
|
- }
|
|
|
- fclose(file_handle);
|
|
|
-}
|
|
|
-
|
|
|
-FILE *open_reply_pipe( char *pipe_name )
|
|
|
-{
|
|
|
-
|
|
|
- int fifofd;
|
|
|
- FILE *file_handle;
|
|
|
- int flags;
|
|
|
-
|
|
|
- int retries=FIFO_REPLY_RETRIES;
|
|
|
-
|
|
|
- if (!pipe_name || *pipe_name==0) {
|
|
|
- DBG("DEBUG: open_reply_pipe: no file to write to about missing cmd\n");
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
-tryagain:
|
|
|
- /* open non-blocking to make sure that a broken client will not
|
|
|
- * block the FIFO server forever */
|
|
|
- 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;
|
|
|
- }
|
|
|
- /* security checks: is this really a fifo?, is
|
|
|
- * it hardlinked? is it a soft link? */
|
|
|
- if (fifo_check(fifofd, pipe_name)<0) goto error;
|
|
|
-
|
|
|
- /* 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));
|
|
|
- goto error;
|
|
|
- }
|
|
|
- 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));
|
|
|
- goto error;
|
|
|
- }
|
|
|
-
|
|
|
- /* create an I/O stream */
|
|
|
- file_handle=fdopen( fifofd, "w");
|
|
|
- if (file_handle==NULL) {
|
|
|
- LOG(L_ERR, "ERROR: open_reply_pipe: open error (%s): %s\n",
|
|
|
- pipe_name, strerror(errno));
|
|
|
- goto error;
|
|
|
- }
|
|
|
- return file_handle;
|
|
|
-error:
|
|
|
- close(fifofd);
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-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;
|
|
|
-
|
|
|
- /* register a diagnostic FIFO command */
|
|
|
- register_core_fifo();
|
|
|
-
|
|
|
- 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) {
|
|
|
- LOG(L_DBG, "INFO: fifo_server: command empty\n");
|
|
|
- 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) {
|
|
|
- LOG(L_ERR, "ERROR: fifo_server: command must begin with %c: %.*s\n",
|
|
|
- CMD_SEPARATOR, line_len, buf );
|
|
|
- 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);
|
|
|
- fifo_reply(file, "500 command '%s' not available\n", command);
|
|
|
- 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);
|
|
|
- DBG("**** done consume\n");
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-int init_fifo_server()
|
|
|
-{
|
|
|
- char *t;
|
|
|
- struct stat filestat;
|
|
|
- int n;
|
|
|
- long opt;
|
|
|
-
|
|
|
- if (fifo==NULL) {
|
|
|
- DBG("DBG: open_fifo_server: no fifo will be opened\n");
|
|
|
- /* everything is ok, we just do not want to start */
|
|
|
- return 1;
|
|
|
- }
|
|
|
- if (strlen(fifo)==0) {
|
|
|
- DBG("DBG: open_fifo_server: fifo disabled\n");
|
|
|
- return 1;
|
|
|
- }
|
|
|
- DBG("DBG: open_uac_fifo: opening fifo...\n");
|
|
|
- n=stat(fifo, &filestat);
|
|
|
- if (n==0){
|
|
|
- /* 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;
|
|
|
- }
|
|
|
- }else if (n<0 && errno!=ENOENT){
|
|
|
- LOG(L_DBG, "DEBUG: open_fifo_server: FIFO stat failed: %s\n",
|
|
|
- strerror(errno));
|
|
|
- }
|
|
|
- /* create FIFO ... */
|
|
|
- if ((mkfifo(fifo, sock_mode)<0)) {
|
|
|
- LOG(L_ERR, "ERROR: open_fifo_server; can't create FIFO: "
|
|
|
- "%s (mode=%d)\n",
|
|
|
- strerror(errno), sock_mode);
|
|
|
- return -1;
|
|
|
- }
|
|
|
- DBG("DEBUG: FIFO created @ %s\n", fifo );
|
|
|
- if ((chmod(fifo, sock_mode)<0)) {
|
|
|
- LOG(L_ERR, "ERROR: open_fifo_server; can't chmod FIFO: "
|
|
|
- "%s (mode=%d)\n",
|
|
|
- strerror(errno), sock_mode);
|
|
|
- return -1;
|
|
|
- }
|
|
|
- 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 );
|
|
|
- time(&up_since);
|
|
|
- t=ctime(&up_since);
|
|
|
- if (strlen(t)+1>=MAX_CTIME_LEN) {
|
|
|
- LOG(L_ERR, "ERROR: open_fifo_server: "
|
|
|
- "too long date %d\n", (int)strlen(t));
|
|
|
- return -1;
|
|
|
- }
|
|
|
- memcpy(up_since_ctime,t,strlen(t)+1);
|
|
|
- /* open it non-blocking or else wait here until someone
|
|
|
- * opens it for writing */
|
|
|
- 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 */
|
|
|
-#ifdef USE_TCP
|
|
|
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd)<0){
|
|
|
- LOG(L_ERR, "ERROR: open_fifo_server: socketpair failed: %s\n",
|
|
|
- strerror(errno));
|
|
|
- return -1;
|
|
|
- }
|
|
|
-#endif
|
|
|
- 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 */
|
|
|
- /* record pid twice to avoid the child using it, before
|
|
|
- * parent gets a chance to set it*/
|
|
|
- pt[process_no].pid=getpid();
|
|
|
- LOG(L_INFO, "INFO: fifo process starting: %d\n", getpid());
|
|
|
- /* call per-child module initialization too -- some
|
|
|
- FIFO commands may need it
|
|
|
- */
|
|
|
-#ifdef USE_TCP
|
|
|
- close(sockfd[0]);
|
|
|
- unix_tcp_sock=sockfd[1];
|
|
|
-#endif
|
|
|
- if (init_child(PROC_FIFO) < 0 ) {
|
|
|
- LOG(L_ERR, "ERROR: open_uac_fifo: init_child failed\n");
|
|
|
- return -1;
|
|
|
- }
|
|
|
- /* a real server doesn't die if writing to reply fifo fails */
|
|
|
- signal(SIGPIPE, SIG_IGN);
|
|
|
- LOG(L_INFO, "SER: open_uac_fifo: fifo server up at %s...\n",
|
|
|
- fifo);
|
|
|
- fifo_server( fifo_stream ); /* never returns */
|
|
|
- }
|
|
|
- /* dad process */
|
|
|
- pt[process_no].pid=fifo_pid;
|
|
|
- strncpy(pt[process_no].desc, "fifo server", MAX_PT_DESC );
|
|
|
-#ifdef USE_TCP
|
|
|
- close(sockfd[1]);
|
|
|
- pt[process_no].unix_sock=sockfd[0];
|
|
|
- pt[process_no].idx=-1; /* this is not "tcp" process*/
|
|
|
-#endif
|
|
|
- return 1;
|
|
|
-}
|
|
|
-
|
|
|
-static int print_version_cmd( FILE *stream, char *response_file )
|
|
|
-{
|
|
|
- if (response_file) {
|
|
|
- fifo_reply(response_file, "200 ok\n" SERVER_HDR CRLF );
|
|
|
- } else {
|
|
|
- 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;
|
|
|
- }
|
|
|
-
|
|
|
- fputs( "200 ok\n", reply_pipe);
|
|
|
- for (p=0; p<my_argc;p++)
|
|
|
- fprintf( reply_pipe, "%s\n", my_argv[p] );
|
|
|
-
|
|
|
- fclose(reply_pipe);
|
|
|
- return 1;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-/* diagnostic and hello-world FIFO command */
|
|
|
-static int print_fifo_cmd( FILE *stream, char *response_file )
|
|
|
-{
|
|
|
- char text[MAX_PRINT_TEXT];
|
|
|
- int text_len;
|
|
|
-
|
|
|
- /* expect one line which will be printed out */
|
|
|
- if (response_file==0 || *response_file==0 ) {
|
|
|
- LOG(L_ERR, "ERROR: print_fifo_cmd: null file\n");
|
|
|
- return -1;
|
|
|
- }
|
|
|
- if (!read_line(text, MAX_PRINT_TEXT, stream, &text_len)) {
|
|
|
- fifo_reply(response_file,
|
|
|
- "500 print_fifo_cmd: too big text");
|
|
|
- return -1;
|
|
|
- }
|
|
|
- /* now the work begins */
|
|
|
- if (response_file) {
|
|
|
- fifo_reply(response_file, "200 ok\n%s\n", text );
|
|
|
- } else {
|
|
|
- LOG(L_INFO, "INFO: print_fifo_cmd: %.*s\n",
|
|
|
- text_len, text );
|
|
|
- }
|
|
|
- return 1;
|
|
|
-}
|
|
|
-
|
|
|
-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;
|
|
|
- }
|
|
|
-
|
|
|
- time(&now);
|
|
|
- fifo_reply( response_file, "200 ok\n"
|
|
|
- "Now: %sUp Since: %sUp time: %.0f [sec]\n",
|
|
|
- ctime(&now), up_since_ctime, difftime(now, up_since) );
|
|
|
-
|
|
|
- return 1;
|
|
|
-}
|
|
|
-
|
|
|
-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;
|
|
|
-}
|
|
|
-
|
|
|
-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) {
|
|
|
- LOG(L_ERR, "ERROR: which_fifo_cmd: opening reply pipe (%s) failed\n",
|
|
|
- response_file );
|
|
|
- return -1;
|
|
|
- }
|
|
|
- fputs( "200 ok\n", reply_pipe);
|
|
|
- for(c=cmd_list; c; c=c->next) {
|
|
|
- fprintf( reply_pipe, "%s\n", c->name );
|
|
|
- }
|
|
|
-
|
|
|
- fclose(reply_pipe);
|
|
|
- return 1;
|
|
|
-}
|
|
|
-
|
|
|
-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;
|
|
|
- }
|
|
|
-
|
|
|
- fputs( "200 ok\n", reply_pipe);
|
|
|
- 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;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-static int meminfo_fifo_cmd( FILE *stream, char *response_file )
|
|
|
-{
|
|
|
- struct mem_info mi;
|
|
|
-
|
|
|
- if (response_file==0 || *response_file==0 ) {
|
|
|
- LOG(L_ERR, "ERROR: meminfo_fifo_cmd: null file\n");
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
-#ifdef SHM_MEM
|
|
|
- shm_info(&mi);
|
|
|
- fifo_reply( response_file, "200 ok\n"
|
|
|
- "total:%ld\n"
|
|
|
- "free:%ld\n"
|
|
|
- "used:%ld\n"
|
|
|
- "max used:%ld\n"
|
|
|
- "fragments:%ld\n",
|
|
|
- mi.total_size, mi.free, mi.real_used,
|
|
|
- mi.max_used, mi.total_frags);
|
|
|
-#else
|
|
|
- fifo_reply( response_file, "400 No shared memory support\n");
|
|
|
-#endif
|
|
|
-
|
|
|
- return 1;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-int register_core_fifo()
|
|
|
-{
|
|
|
- if (register_fifo_cmd(print_fifo_cmd, FIFO_PRINT, 0)<0) {
|
|
|
- LOG(L_ERR, "ERROR: unable to register '%s' FIFO cmd\n", FIFO_PRINT);
|
|
|
- return -1;
|
|
|
- }
|
|
|
- if (register_fifo_cmd(uptime_fifo_cmd, FIFO_UPTIME, 0)<0) {
|
|
|
- LOG(L_ERR, "ERROR: unable to register '%s' FIFO cmd\n", FIFO_UPTIME);
|
|
|
- return -1;
|
|
|
- }
|
|
|
- if (register_fifo_cmd(print_version_cmd, FIFO_VERSION, 0)<0) {
|
|
|
- LOG(L_ERR, "ERROR: unable to register '%s' FIFO cmd\n",FIFO_VERSION);
|
|
|
- return -1;
|
|
|
- }
|
|
|
- if (register_fifo_cmd(pwd_cmd, FIFO_PWD, 0)<0) {
|
|
|
- LOG(L_ERR, "ERROR: unable to register '%s' FIFO cmd\n", FIFO_PWD);
|
|
|
- return -1;
|
|
|
- }
|
|
|
- if (register_fifo_cmd(arg_cmd, FIFO_ARG, 0)<0) {
|
|
|
- LOG(L_ERR, "ERROR: unable to register '%s' FIFO cmd\n", FIFO_ARG);
|
|
|
- return -1;
|
|
|
- }
|
|
|
- if (register_fifo_cmd(which_fifo_cmd, FIFO_WHICH, 0)<0) {
|
|
|
- LOG(L_ERR, "ERROR: unable to register '%s' FIFO cmd\n", FIFO_WHICH);
|
|
|
- return -1;
|
|
|
- }
|
|
|
- if (register_fifo_cmd(ps_fifo_cmd, FIFO_PS, 0)<0) {
|
|
|
- LOG(L_ERR, "ERROR: unable to register '%s' FIFO cmd\n", FIFO_PS);
|
|
|
- return -1;
|
|
|
- }
|
|
|
- if (register_fifo_cmd(kill_fifo_cmd, FIFO_KILL, 0)<0) {
|
|
|
- LOG(L_CRIT, "ERROR: unable to register '%s' FIFO cmd\n", FIFO_KILL);
|
|
|
- return -1;
|
|
|
- }
|
|
|
- if (register_fifo_cmd(meminfo_fifo_cmd, FIFO_MEMINFO, 0)<0) {
|
|
|
- LOG(L_CRIT, "ERROR: unable to register '%s' FIFO cmd\n", FIFO_MEMINFO);
|
|
|
- return -1;
|
|
|
- }
|
|
|
- if (fifo_db_url==0) {
|
|
|
- LOG(L_WARN,"WARNING: no fifo_db_url given - "
|
|
|
- "fifo DB commands disabled!\n");
|
|
|
- } else if (init_db_fifo(fifo_db_url)<0){
|
|
|
- return -1;
|
|
|
- }
|
|
|
- return 1;
|
|
|
-}
|