/* set send/receive timeouts on socket(s) */ int set_timeouts( int rsock, int ssock, u_short rcv_tmout_sec, u_short rcv_tmout_usec, u_short snd_tmout_sec, u_short snd_tmout_usec ) { struct timeval rtv, stv; int rc = 0; if( rsock ) { rtv.tv_sec = rcv_tmout_sec; rtv.tv_usec = rcv_tmout_usec; rc = setsockopt( rsock, SOL_SOCKET, SO_RCVTIMEO, &rtv, sizeof(rtv) ); if( -1 == rc ) { mperror(g_flog, errno, "%s: setsockopt - SO_RCVTIMEO", __func__); return ERR_INTERNAL; } TRACE( (void)tmfprintf (g_flog, "socket %d: RCV timeout set to %ld sec, %ld usec\n", rsock, rtv.tv_sec, rtv.tv_usec) ); } if( ssock ) { stv.tv_sec = snd_tmout_sec; stv.tv_usec = snd_tmout_usec; rc = setsockopt( ssock, SOL_SOCKET, SO_SNDTIMEO, &stv, sizeof(stv) ); if( -1 == rc ) { mperror(g_flog, errno, "%s: setsockopt - SO_SNDTIMEO", __func__); return ERR_INTERNAL; } TRACE( (void)tmfprintf (g_flog, "socket %d: SEND timeout set to %ld sec, %ld usec\n", rsock, rtv.tv_sec, rtv.tv_usec) ); } return rc; }
/* subscribe to the (configured) multicast channel */ static int subscribe( int* sockfd, struct in_addr* mcast_inaddr ) { struct sockaddr_in sa; const char* ipaddr = g_recopt.rec_channel; size_t rcvbuf_len = 0; int rc = 0; assert( sockfd && mcast_inaddr ); if( 1 != inet_aton( ipaddr, &sa.sin_addr ) ) { mperror( g_flog, errno, "%s: Invalid subscription [%s:%d]: inet_aton", __func__, ipaddr, g_recopt.rec_port ); return -1; } sa.sin_family = AF_INET; sa.sin_port = htons( (uint16_t)g_recopt.rec_port ); if( 1 != inet_aton( g_recopt.mcast_addr, mcast_inaddr ) ) { mperror( g_flog, errno, "%s: Invalid multicast interface: [%s]: inet_aton", __func__, g_recopt.mcast_addr ); return -1; } rc = calc_buf_settings( NULL, &rcvbuf_len ); if (0 != rc) return rc; return setup_mcast_listener( &sa, mcast_inaddr, sockfd, (g_recopt.nosync_sbuf ? 0 : rcvbuf_len) ); }
/* set/clear file/socket's mode as non-blocking */ int set_nblock( int fd, int set ) { int flags = 0; flags = fcntl( fd, F_GETFL, 0 ); if( flags < 0 ) { mperror( g_flog, errno, "%s: fcntl() getting flags on fd=[%d]", __func__, fd ); return -1; } if( set ) flags |= O_NONBLOCK; else flags &= ~O_NONBLOCK; if( fcntl( fd, F_SETFL, flags ) < 0 ) { mperror( g_flog, errno, "%s: fcntl() %s non-blocking mode " "on fd=[%d]", __func__, (set ? "setting" : "clearing"), fd ); return -1; } return 0; }
/* ---------------- file_no_cache ----------------------------- * When we read sequentially from a huge series of structure * files, the OS tries to cache them all, although we will not * reuse them in the near future. * This is not harmless. When running in the background, wurst * tends to fill the cache with this data, wipe out files that a * user really does want to work with. * This is a wrapper around the posix routine which should be * called on all files like this (structures, profiles, ...). * The routine may not be provided everywhere, so put any * portability checks in here. * To be sure, maybe I should also call the function * with POSIX_FADV_DONTNEED. * Return * 0 if all went well * errno if something broke. */ int file_no_cache (FILE *fp) { # ifdef POSIX_FADV_SEQUENTIAL int fnum, r, s; struct stat statbuf; const off_t offset = 0; const size_t len = 0; const char *this_sub = "file_no_cache"; int e = 0; if ((fnum = fileno (fp)) == -1) { mperror (this_sub); return ( -1 ); } if (fstat ( fnum, &statbuf) == -1) { mperror (this_sub); return ( -1 ); } if ( S_ISFIFO (statbuf.st_mode)) /* If this is a pipe,*/ return 0; /* then just return */ if ((r = posix_fadvise (fnum, offset, len, POSIX_FADV_SEQUENTIAL))) { e = errno; err_printf (this_sub, "POSIX_FADV_SEQUENTIAL probably broken.\n"); } if ((s = posix_fadvise (fnum, offset, len, POSIX_FADV_NOREUSE))) { e = errno; err_printf (this_sub, "POSIX_FADV_NOREUSE probably broken.\n"); } if (r != 0 || s!= 0) return e; # endif /* POSIX_FADV_SEQUENTIAL */ return 0; }
/* read HTTP request from sockfd, parse it into command * and its parameters (for instance, command='udp' and * parameters being '192.168.0.1:5002') */ static int read_command( int sockfd, struct server_ctx *srv) { #define DBUF_SZ 2048 /* max size for raw data with HTTP request */ #define RBUF_SZ 512 /* max size for url-derived request */ char httpbuf[ DBUF_SZ ] = "\0", request[ RBUF_SZ ] = "\0"; ssize_t hlen; size_t rlen; int rc = 0; assert( (sockfd > 0) && srv ); TRACE( (void)tmfprintf( g_flog, "Reading command from socket [%d]\n", sockfd ) ); usleep(50); /* ..workaround VLC behavior: wait for receiving entire HTTP request, one packet per line */ hlen = recv( sockfd, httpbuf, sizeof(httpbuf), 0 ); if( 0>hlen ) { rc = errno; if( !no_fault(rc) ) mperror(g_flog, rc, "%s - recv (%d)", __func__, rc); else { TRACE( mperror(g_flog, rc, "%s - recv (%d)", __func__, rc) ); } return rc; } if (0 == hlen) { (void) tmfprintf (g_flog, "%s: client closed socket [%d]\n", __func__, sockfd); return 1; } /* DEBUG - re-enable if needed */ TRACE( (void)tmfprintf( g_flog, "HTTP buffer [%ld bytes] received\n%s", (long)hlen, httpbuf ) ); /* TRACE( (void) save_buffer( httpbuf, hlen, "/tmp/httpbuf.dat" ) ); */ rlen = sizeof(request); rc = get_request( httpbuf, (size_t)hlen, request, &rlen ); if (rc) return rc; TRACE( (void)tmfprintf( g_flog, "Request=[%s], length=[%lu]\n", request, (u_long)rlen ) ); rc = parse_auth( httpbuf, (size_t)hlen ); TRACE( (void)tmfprintf( g_flog, "Auth result=[%d]\n", rc ) ); if (rc) return rc; (void) memset( &srv->rq, 0, sizeof(srv->rq) ); rc = parse_param( request, rlen, srv->rq.cmd, sizeof(srv->rq.cmd), srv->rq.param, sizeof(srv->rq.param), srv->rq.tail, sizeof(srv->rq.tail) ); if( 0 == rc ) { TRACE( (void)tmfprintf( g_flog, "Command [%s] with params [%s], tail [%s]" " read from socket=[%d]\n", srv->rq.cmd, srv->rq.param, srv->rq.tail, sockfd) ); } return rc; }
/* send server status as HTTP response to the given socket */ static int report_status( int sockfd, const struct server_ctx* ctx, int options ) { char *buf = NULL; int rc = 0; ssize_t n, nsent; size_t nlen = 0, bufsz, i; struct client_ctx *clc = NULL; enum {BYTES_HDR = 2048, BYTES_PER_CLI = 256}; assert( (sockfd > 0) && ctx ); bufsz = BYTES_HDR; for (i = 0, clc=ctx->cl; i < ctx->clmax; ++i, ++clc) { if( ctx->cl[i].pid > 0 ) bufsz += BYTES_PER_CLI + strlen(clc->tail); } buf = malloc(bufsz); if( !buf ) { mperror(g_flog, ENOMEM, "malloc for %ld bytes for HTTP buffer " "failed in %s", (long)bufsz, __func__ ); return ERR_INTERNAL; } (void) memset( buf, 0, sizeof(bufsz) ); nlen = bufsz; rc = mk_status_page( ctx, buf, &nlen, options | MSO_HTTP_HEADER ); for( n = nsent = 0; (0 == rc) && (nsent < (ssize_t)nlen); ) { errno = 0; n = send( sockfd, buf, (int)nlen, 0 ); if( (-1 == n) && (EINTR != errno) ) { mperror(g_flog, errno, "%s: send", __func__); rc = ERR_INTERNAL; break; } nsent += n; } if( 0 != rc ) { TRACE( (void)tmfprintf( g_flog, "Error generating status report\n" ) ); } else { /* DEBUG only TRACE( (void)tmfprintf( g_flog, "Saved status buffer to file\n" ) ); TRACE( (void)save_buffer(buf, nlen, "/tmp/status-udpxy.html") ); */ } free(buf); return rc; }
/* determine type of stream saved in file */ upxfmt_t get_fstream_type( int fd, FILE* log ) { ssize_t n = 0; off_t offset = 0, where = 0; upxfmt_t dtype = UPXDT_UNKNOWN; char* data = NULL; /* read in enough data to contain extended header * and beginning of payload segment */ size_t len = TS_SEG_LEN + RTP_XTHDRLEN; assert( (fd > 0) && log ); if( NULL == (data = malloc( len )) ) { mperror( log, errno, "%s: malloc", __func__ ); return UPXDT_UNKNOWN; } do { /* check if it is a MPEG TS stream */ n = read( fd, data, len ); if( 0 != sizecheck( "Not enough space for stream data", len, n, log, __func__ ) ) break; offset += n; dtype = get_mstream_type( data, len, log ); if( UPXDT_UNKNOWN == dtype ) { TRACE( (void)tmfprintf( log, "%s: file type is not recognized\n", __func__ ) ); dtype = UPXDT_UNKNOWN; break; } } while(0); if( NULL != data ) free( data ); if( n <= 0 ) { mperror( log, errno, "%s", __func__ ); return UPXDT_UNKNOWN; } where = lseek( fd, (-1) * offset, SEEK_CUR ); if( -1 == where ) { mperror( log, errno, "%s: lseek", __func__ ); return UPXDT_UNKNOWN; } /* TRACE( (void)tmfprintf( log, "%s: stream type = [%d]=[%s]\n", __func__, (int)dtype, fmt2str(dtype) ) ); */ return dtype; }
/* \fcnfh Open database and auxiliary file, set all FILE pointers. */ static int db_open(char *dbfilenameo, char *dbaux) { dbfilename = strdup(dbfilenameo); //Open database if((fp = fopen(dbfilename,"r")) == NULL) mperror(MSGP_USER, "Could not open file '%s' for reading\n" , dbfilename); int maxlen=50; char line[maxlen], *lp; const char *id = "#TLI-ASCII"; if(fgets(line, maxlen-1, fp) && strncmp(line, id, strlen(id)) != 0) mperror(MSGP_SYSTEM, "File '%s' does not have the proper " "TLI-ASCII heading, but it was approved " "by db find(?)\n" , dbfilename); currline = 1; size_t pos = ftell(fp); char rc; settoolongerr(&linetoolong_text,dbfilename,&currline); //skip comments and blank lines while((rc=fgetupto(line,maxline,fp)) == '#' || rc == '\n') currline++; if(!rc) earlyend(dbfilename, currline); //get number of database which needs to be one at this point. If //omitted number of databases, it is assumed to be 1 if (line[0] == 'd'){ ndb = strtol(line+1, &lp, 0); if (lp == line+1) mperror(MSGP_USER, "Invalid number of databases in TLI-ASCII '%s'\n" , dbfilename); } else{ currline = ndb = 1; fseek(fp, pos, SEEK_SET); } return LR_OK; }
/* make sure the channel is valid: subscribe/close */ static int verify_channel() { struct in_addr mcast_inaddr; int sockfd = -1, rc = -1; char buf[16]; ssize_t nrd = -1; struct timeval rtv; static const time_t MSOCK_TMOUT_SEC = 2; rc = subscribe( &sockfd, &mcast_inaddr ); do { if( rc ) break; rtv.tv_sec = MSOCK_TMOUT_SEC; rtv.tv_usec = 0; rc = setsockopt( sockfd, SOL_SOCKET, SO_RCVTIMEO, &rtv, sizeof(rtv) ); if( -1 == rc ) { mperror(g_flog, errno, "%s: setsockopt - SO_RCVTIMEO", __func__); rc = ERR_INTERNAL; break; } /* attempt to read from the socket to * make sure the channel is alive */ nrd = read( sockfd, buf, sizeof(buf) ); if( nrd <= 0 ) { rc = errno; mperror( g_flog, errno, "channel read" ); if( EAGAIN == rc ) { (void) tmfprintf( g_flog, "failed to read from [%s:%d]\n", g_recopt.rec_channel, g_recopt.rec_port ); } rc = ERR_INTERNAL; break; } TRACE( (void)tmfprintf( g_flog, "%s: read [%ld] bytes " "from source channel\n", __func__, nrd ) ); } while(0); if( sockfd >= 0 ) { close_mcast_listener( sockfd, &mcast_inaddr ); } return rc; }
static int get_sockbuf_size( int sockfd, int option, size_t* const len, const char* bufname ) { int rc = 0; size_t buflen = 0; socklen_t varsz = sizeof(buflen); assert( sockfd && len && bufname ); rc = getsockopt( sockfd, SOL_SOCKET, option, &buflen, &varsz ); if (0 != rc) { mperror( g_flog, errno, "%s: getsockopt (%s) [%d]", __func__, bufname, option); return -1; } else { TRACE( (void)tmfprintf( g_flog, "current %s buffer size is [%u] bytes for socket [%d]\n", bufname, buflen, sockfd ) ); *len = buflen; } return rc; }
/* write data to destination(s) */ ssize_t write_data( const struct dstream_ctx* spc, const char* data, const ssize_t len, int fd ) { ssize_t n = 0, error = IO_ERR; int32_t n_count = -1; assert( spc && data && len ); if( fd <= 0 ) return 0; if( spc->flags & F_SCATTERED ) { n_count = spc->pkt_count; n = writev( fd, spc->pkt, n_count ); if( n <= 0 ) { if( EAGAIN == errno ) { (void)tmfprintf( g_flog, "Write on fd=[%d] timed out\n", fd); error = IO_BLK; } mperror( g_flog, errno, "%s: writev", __func__ ); return error; } } else { n = write_buf( fd, data, len, g_flog ); if( n < 0 ) error = n; } return (n > 0) ? n : error; }
/* clear SIGCHLD flag and adjust context if needed */ static void wait_children( struct server_ctx* ctx, int options ) { int status, n = 0; pid_t pid; assert( ctx ); if (0 == g_childexit) { TRACE( (void)tmfputs ("No children exited since last check\n", g_flog) ); return; } g_childexit = 0; TRACE( (void)tmfputs ("Waiting on exited children\n", g_flog) ); while( 0 < (pid = waitpid( -1, &status, options )) ) { TRACE( (void)tmfprintf( g_flog, "Client [%d] has exited.\n", pid) ); delete_client( ctx, pid ); ++n; } if( (-1 == pid) && ( ECHILD != errno ) ) { mperror(g_flog, errno, "%s: waitpid", __func__); } if (n > 0) { TRACE( (void)tmfprintf (g_flog, "Cleaned up %d children, " "%ld still running\n", n, (long)(ctx->clmax - ctx->clfree)) ); } return; }
/* send HTTP response to socket */ static int send_http_response( int sockfd, int code, const char* reason) { static char msg[ 3072 ]; ssize_t nsent; a_socklen_t msglen; static const char CONTENT_TYPE[] = "Content-Type:application/octet-stream"; assert( (sockfd > 0) && code && reason ); msg[0] = '\0'; if ((200 == code) && g_uopt.h200_ftr[0]) { msglen = snprintf( msg, sizeof(msg) - 1, "HTTP/1.1 %d %s\r\nServer: %s\r\n%s\r\n%s\r\n\r\n", code, reason, g_udpxy_finfo, CONTENT_TYPE, g_uopt.h200_ftr); } else { msglen = snprintf( msg, sizeof(msg) - 1, "HTTP/1.1 %d %s\r\nServer: %s\r\n%s\r\n\r\n", code, reason, g_udpxy_finfo, CONTENT_TYPE ); } if( msglen <= 0 ) return ERR_INTERNAL; nsent = send( sockfd, msg, msglen, 0 ); if( -1 == nsent ) { mperror(g_flog, errno, "%s - send", __func__); return ERR_INTERNAL; } TRACE( (void)tmfprintf( g_flog, "Sent HTTP response code=[%d], " "reason=[%s] to socket=[%d]\n%s\n", code, reason, sockfd, msg) ); return 0; }
/* initialize server context data */ int init_server_ctx( struct server_ctx* ctx, const size_t max, const char* laddr, uint16_t lport, const char* mifc_addr ) { int flags = -1; assert( lport && mifc_addr && ctx && max ); ctx->lsockfd = 0; (void) strncpy( ctx->listen_addr, (laddr ? laddr : IPv4_ALL), IPADDR_STR_SIZE ); ctx->listen_addr[ IPADDR_STR_SIZE - 1 ] = '\0'; ctx->listen_port = lport; (void) strncpy( ctx->mcast_ifc_addr, mifc_addr, IPADDR_STR_SIZE ); ctx->mcast_ifc_addr[ IPADDR_STR_SIZE - 1 ] = '\0'; ctx->cl = calloc(max, sizeof(struct client_ctx)); if( NULL == ctx->cl ) { mperror( g_flog, errno, "%s: client_t - calloc", __func__ ); return ERR_INTERNAL; } (void) memset( ctx->cl, 0, max * sizeof(struct client_ctx) ); ctx->clfree = ctx->clmax = max; (void) memset( &ctx->rq, 0, sizeof(ctx->rq) ); if( 0 != pipe(ctx->cpipe) ) { mperror( g_flog, errno, "%s: pipe", __func__ ); return ERR_INTERNAL; } /* make reading end of pipe non-blocking (we don't want to * block on pipe read on the server side) */ if( -1 == (flags = fcntl( ctx->cpipe[0], F_GETFL )) || -1 == fcntl( ctx->cpipe[0], F_SETFL, flags | O_NONBLOCK ) ) { mperror( g_flog, errno, "%s: fcntl", __func__ ); return ERR_INTERNAL; } return 0; }
/* \fcnfh This function is called if a line of 'file' was longer than 'max' characters */ void linetoolong(int max, /* Maxiumum length of an accepted line */ char *file, /* File from which we were reading */ int line){ /* Line who was being read */ mperror(MSGP_USER|MSGP_ALLOWCONT, "Line %i of file '%s' has more than %i characters.\n", file, max); exit(EXIT_FAILURE); }
int main(void){ char *argv[] = {"/bin/lsls", NULL}; if(execve("/bin/lsls", argv, NULL) == -1) { mperror("execve", errno); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); }
/* \fcnfh It outputs error. Used when EOF is found before expected */ static void earlyend(char *file, long lin) { mperror(MSGP_USER|MSGP_ALLOWCONT, "readlineinfo:: EOF unexpectedly found at line %i in\n" "ascii-TLI linedb info file '%s'\n" ,lin,file); lineread_free(); exit(EXIT_FAILURE); }
static int db_open(char *dbname, char *dbaux) { if((fp = fopen(dbname,"r")) == NULL) mperror(MSGP_USER, "Could not open file '%s' for reading\n" , dbname); return LR_OK; }
/* ---------------- mfopen ----------------------------------- * Wrapper around fopen. Last arg is a string, typically the * name of the calling function which will be printed out at the * start of error messages. */ FILE * mfopen (const char *fname, const char *mode, const char *s) { FILE *fp; if ((fp = fopen (fname, mode)) == NULL) { int e = errno; err_printf (s, "Open fail on %s\n", fname); errno = e; mperror (s); } return fp; }
static int xfwrt (const void *data, size_t size, size_t nmem, FILE *fp, const char *caller, const char *fname) { size_t r = fwrite(data, size, nmem, fp) ; if (r != nmem) { mperror (caller); err_printf (caller, "Trying to write to %s\n", fname); return EXIT_FAILURE; } return EXIT_SUCCESS; }
/* make current process run as a daemon */ int daemonize(int options, FILE* log) { pid_t pid; int rc = 0, fh = -1; assert( log ); if( (pid = fork()) < 0 ) { mperror( log, errno, "%s: fork", __func__); return -1; } else if( 0 != pid ) { exit(0); } do { if( -1 == (rc = setsid()) ) { mperror( log, errno, "%s: setsid", __func__); break; } if( -1 == (rc = chdir("/")) ) { mperror( log, errno, "%s: chdir", __func__ ); break; } (void) umask(0); if( !(options & DZ_STDIO_OPEN) ) { for( fh = 0; fh < 3; ++fh ) if( -1 == (rc = close(fh)) ) { mperror( log, errno, "%s: close", __func__); break; } } if( SIG_ERR == signal(SIGHUP, SIG_IGN) ) { mperror( log, errno, "%s: signal", __func__ ); rc = 2; break; } } while(0); if( 0 != rc ) return rc; /* child exits to avoid session leader's re-acquiring * control terminal */ if( (pid = fork()) < 0 ) { mperror( log, errno, "%s: fork", __func__); return -1; } else if( 0 != pid ) exit(0); return 0; }
/* register received packet into registry (for scattered output) */ static int register_packet( struct dstream_ctx* spc, char* buf, size_t len ) { struct iovec* new_pkt = NULL; static const int DO_VERIFY = 1; void* new_buf = buf; size_t new_len = len; assert( spc->max_pkt > 0 ); /* enlarge packet registry if needed */ if( spc->pkt_count >= spc->max_pkt ) { spc->max_pkt <<= 1; spc->pkt = realloc( spc->pkt, spc->max_pkt * sizeof(spc->pkt[0]) ); if( NULL == spc->pkt ) { mperror( g_flog, errno, "%s: realloc", __func__ ); return -1; } TRACE( (void)tmfprintf( g_flog, "RTP packet registry " "expanded to [%lu] records\n", (u_long)spc->max_pkt ) ); } /* put packet info into registry */ new_pkt = &(spc->pkt[ spc->pkt_count ]); /* TRACE( (void)tmfprintf( stderr, "IN: packet [%lu]: buf=[%p], len=[%lu]\n", (u_long)spc->pkt_count, (void*)buf, (u_long)len ) ); */ if( 0 != RTP_process( &new_buf, &new_len, DO_VERIFY, g_flog ) ) { TRACE( (void)tmfputs("register packet: dropping\n", g_flog) ); spc->flags |= F_DROP_PACKET; return 0; } new_pkt->iov_base = new_buf; new_pkt->iov_len = new_len; /* TRACE( (void)tmfprintf( stderr, "OUT: packet [%lu]: buf=[%p], len=[%lu]\n", (u_long)spc->pkt_count, new_pkt->iov_base, (u_long)new_pkt->iov_len ) ); */ spc->pkt_count++; return 0; }
/* read client statistics data and update the context */ int tpstat_read( struct server_ctx* ctx ) { int cindex = -1; int readfd = -1; ssize_t nread = 0; struct tput_stat ts; struct client_ctx* client = NULL; assert( ctx ); readfd = ctx->cpipe[0]; assert( readfd > 0 ); (void)memset( &ts, 0, sizeof(ts) ); nread = read( readfd, &ts, sizeof(ts) ); if( nread <= 0 ) { if( (EINTR != errno) && (EAGAIN != errno) ) { mperror( g_flog, errno, "%s: read", __func__ ); return ERR_INTERNAL; } /* if it's an interrupt or no data available - ignore */ TRACE( (void)tmfprintf( g_flog, "%s - read error [%s] - ingored\n", __func__, strerror(errno)) ); return 0; } if( sizeof(ts) != (size_t)nread ) { (void)tmfprintf( g_flog, "%s - read [%d] bytes from pipe, expected [%u]\n", __func__, nread, (int)sizeof(ts) ); return ERR_INTERNAL; } TRACE( (void)tmfprintf( g_flog, "Received TSTAT={ sender=[%ld], bytes=[%f], seconds=[%f] }\n", (long)ts.sender_id, ts.nbytes, ts.nsec) ); cindex = find_client( ctx, (pid_t)ts.sender_id ); if( -1 == cindex ) { (void)tmfprintf( g_flog, "%s - cannot find client [%ld]\n", __func__, (long)ts.sender_id ); /* ignore invalid client id's */ return 0; } client = &(ctx->cl[ cindex ]); client->tstat = ts; TRACE( (void)tmfprintf( g_flog, "Updated context for pid=[%d]; " "[%.1f] Kb/sec\n", client->pid, (ts.nbytes / 1024) / ts.nsec ) ); return 0; }
/* \fcnfh print out an error, it is called by readdatarng if one of the field with transition info is invalid @returns -5 always */ static int invalidfield(char *line, /* Contents of the line */ char *file, /* File name */ int nmb, /* File number */ int fld, /* field with the error */ char *fldn) /* Name of the field */ { mperror(MSGP_USER|MSGP_ALLOWCONT, "Line %i of file '%s': Field %i (%s) has\n" " not a valid value:\n%s\n" ,nmb,file,fld,fldn,line); return -5; }
/* send HTTP response to socket */ static int send_http_response( int sockfd, int code, const char* reason) { static char msg[ 3072 ]; ssize_t nsent; a_socklen_t msglen; int err = 0; assert( (sockfd > 0) && code && reason ); msg[0] = '\0'; if ((200 == code) && g_uopt.h200_ftr[0]) { msglen = snprintf( msg, sizeof(msg) - 1, "HTTP/1.1 %d %s\r\nServer: %s\r\n%s\r\n%s\r\n\r\n", code, reason, g_app_info, g_uopt.cnt_type, g_uopt.h200_ftr); } else { msglen = snprintf( msg, sizeof(msg) - 1, "HTTP/1.1 %d %s\r\nServer: %s\r\n%s\r\n\r\n", code, reason, g_app_info, g_uopt.cnt_type ); } if( msglen <= 0 ) return ERR_INTERNAL; nsent = send( sockfd, msg, msglen, 0 ); if( -1 == nsent ) { err = errno; if( !no_fault(err) ) mperror(g_flog, err, "%s - send", __func__); else { TRACE( mperror(g_flog, err, "%s - send", __func__) ); } return ERR_INTERNAL; } TRACE( (void)tmfprintf( g_flog, "Sent HTTP response code=[%d], " "reason=[%s] to socket=[%d]\n%s\n", code, reason, sockfd, msg) ); return 0; }
/* populate connection's source info in client context */ static int get_src_info( struct client_ctx* cl, int sockfd ) { struct stat st; struct sockaddr_in addr; a_socklen_t len = 0; int rc = 0; assert( cl ); if( -1 == (rc = fstat( sockfd, &st )) ) { mperror( g_flog, errno, "%s: fstat", __func__ ); return ERR_INTERNAL; } if( S_ISREG( st.st_mode ) ) { (void) strncpy( cl->src_addr, "File", sizeof(cl->src_addr) ); cl->src_addr[ sizeof(cl->src_addr) - 1 ] = '\0'; cl->src_port = 0; } else if( S_ISSOCK( st.st_mode ) ) { len = sizeof(addr); rc = getpeername( sockfd, (struct sockaddr*)&addr, &len ); if( 0 == rc ) { (void)strncpy( cl->src_addr, inet_ntoa(addr.sin_addr), sizeof(cl->src_addr) - 1 ); cl->src_addr[ sizeof(cl->src_addr) - 1 ] = '\0'; cl->src_port = ntohs(addr.sin_port); } else { mperror( g_flog, errno, "%s: getpeername", __func__ ); return ERR_INTERNAL; } } /* S_ISSOCK */ return rc; }
/* read text file into a buffer * */ ssize_t txtf_read (const char* fpath, char* dst, size_t maxlen, FILE* log) { int rc = 0, fd = -1; ssize_t n = 0; ssize_t left = maxlen - 1; char *p = dst; assert (fpath && dst && maxlen); fd = open (fpath, O_RDONLY, 0); if (-1 == fd) { mperror (log, errno, "%s open %s", __func__, fpath); return -1; } while (left > 0) { n = read (fd, p, maxlen - 1); if (!n) break; if (n < 0) { n = 0; rc = errno; if (EINTR != rc) { mperror (log, errno, "%s read %s", __func__, fpath); break; } rc = 0; } left -= (size_t)n; p += n; } if (!rc) *p = '\0'; if (-1 == close (fd)) { mperror (log, errno, "%s close %s", __func__, fpath); } return (rc ? -1 : ((ssize_t)maxlen - left - 1)); }
/* write buffer to designated socket/file */ ssize_t write_buf( int fd, const char* data, const ssize_t len, FILE* log ) { ssize_t n = 0, nwr = 0, error = IO_ERR; int err = 0; for( n = 0; errno = 0, n < len ; ) { nwr = write( fd, &(data[n]), len - n ); if( nwr <= 0 ) { err = errno; if( EINTR == err ) { TRACE( (void)tmfprintf( log, "%s interrupted\n", __func__ ) ); continue; } else { if( would_block(err) ) error = IO_BLK; break; } } n += nwr; if( nwr != len ) { if( NULL != log ) { TRACE( (void)tmfprintf( log, "Fragment written %s[%ld:%ld]/[%ld] bytes\n", (len > n ? "P" : "F"), (long)nwr, (long)n, (long)len ) ); } } } if( nwr <= 0 ) { if( log ) { if (IO_BLK == error) (void)tmfprintf( log, "%s: socket time-out on write", __func__); else if( !no_fault(err) || g_uopt.is_verbose ) mperror( log, errno, "%s: write", __func__ ); } return error; } return n; }
/* ---------------- xfrd ------------------------------------- * Noisy wrapper around fread. Make it more convenient to * print out the sys error message and name of the file. * Return EXIT_SUCCESS/FAILURE. */ static int xfrd (void *data, size_t size, size_t nmem, FILE *fp, const char *caller, const char *fname) { size_t r = fread (data, size, nmem, fp); if (r != nmem) { if (errno) mperror (caller); err_printf (caller, "Trying to read from %s\n", fname); err_printf (caller, "Wanted %u elements, got %u\n", (unsigned) nmem, (unsigned) r); return EXIT_FAILURE; } return EXIT_SUCCESS; }
/* read HTTP request from sockfd, parse it into command * and its parameters (for instance, command='udp' and * parameters being '192.168.0.1:5002') */ static int read_command( int sockfd, char* cmd, size_t clen, char* param, size_t plen ) { #define DBUF_SZ 2048 /* max size for raw data with HTTP request */ #define RBUF_SZ 512 /* max size for url-derived request */ char httpbuf[ DBUF_SZ ] = "\0", request[ RBUF_SZ ] = "\0"; ssize_t hlen; size_t rlen; int rc = 0; assert( (sockfd > 0) && cmd && clen && param && plen ); cmd[0] = '\0'; /* forget previous command */ TRACE( (void)tmfprintf( g_flog, "Reading command from socket [%d]\n", sockfd ) ); hlen = recv( sockfd, httpbuf, sizeof(httpbuf), 0 ); if( 0>hlen ) { rc = errno; mperror(g_flog, rc, "%s - recv (%d)", __func__, rc); return rc; } if (0 == hlen) { (void) tmfprintf (g_flog, "%s: client closed socket [%d]\n", __func__, sockfd); return 1; } /* DEBUG - re-enable if needed */ TRACE( (void)tmfprintf( g_flog, "HTTP buffer [%ld bytes] received\n%s", (long)hlen, httpbuf ) ); /* TRACE( (void) save_buffer( httpbuf, hlen, "/tmp/httpbuf.dat" ) ); */ rlen = sizeof(request); rc = get_request( httpbuf, (size_t)hlen, request, &rlen ); if (rc) return rc; TRACE( (void)tmfprintf( g_flog, "Request=[%s], length=[%lu]\n", request, (u_long)rlen ) ); rc = parse_param( request, rlen, cmd, clen, param, plen ); if( 0 == rc ) { TRACE( (void)tmfprintf( g_flog, "Command [%s] with params [%s]" " read from socket=[%d]\n", cmd, param, sockfd) ); } return rc; }