1 /* @(#) udpxrec utility: main module 2 * 3 * Copyright 2008-2011 Pavel V. Cherenkov (pcherenkov@gmail.com) 4 * 5 * This file is part of udpxy. 6 * 7 * udpxy is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation, either version 3 of the License, or 10 * (at your option) any later version. 11 * 12 * udpxy is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with udpxy. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 #include <stdio.h> 22 #include <unistd.h> 23 #include <string.h> 24 #include <strings.h> 25 #include <signal.h> 26 #include <errno.h> 27 #include <assert.h> 28 #include <stdlib.h> 29 30 #include <sys/socket.h> 31 #include <arpa/inet.h> 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 35 #ifndef __USE_LARGEFILE64 36 #define __USE_LARGEFILE64 37 #endif 38 39 #ifndef __USE_FILE_OFFSET64 40 #define __USE_FILE_OFFSET64 41 #endif 42 43 #include <fcntl.h> 44 45 #include "osdef.h" /* os-specific definitions */ 46 #include "udpxy.h" 47 #include "util.h" 48 #include "uopt.h" 49 #include "ctx.h" 50 #include "dpkt.h" 51 #include "mtrace.h" 52 #include "netop.h" 53 #include "ifaddr.h" 54 55 56 /* external globals */ 57 58 extern FILE* g_flog; 59 extern volatile sig_atomic_t g_quit; 60 extern const char g_udpxrec_app[]; 61 62 static volatile sig_atomic_t g_alarm = 0; 63 64 /* globals 65 */ 66 67 struct udpxrec_opt g_recopt; 68 static char g_app_info[ 80 ] = {0}; 69 70 /* handler for signals requestin application exit 71 */ 72 static void 73 handle_quitsigs(int signo) 74 { 75 g_quit = 1; 76 if( SIGALRM == signo ) g_alarm = 1; 77 78 TRACE( (void)tmfprintf( g_flog, 79 "*** Caught SIGNAL [%d] in process=[%d] ***\n", 80 signo, getpid()) ); 81 return; 82 } 83 84 85 /* return 1 if the application must gracefully quit 86 */ 87 static sig_atomic_t 88 must_quit() { return g_quit; } 89 90 91 static void 92 usage( const char* app, FILE* fp ) 93 { 94 extern const char UDPXY_COPYRIGHT_NOTICE[]; 95 extern const char UDPXY_CONTACT[]; 96 97 (void) fprintf(fp, "%s\n", g_app_info); 98 (void) fprintf(fp, "usage: %s [-v] [-b begin_time] [-e end_time] " 99 "[-M maxfilesize] [-p pidfile] [-B bufsizeK] [-n nice_incr] " 100 "[-m mcast_ifc_addr] [-l logfile] " 101 "-c src_addr:port dstfile\n", 102 app ); 103 104 (void) fprintf(fp, 105 "\t-v : enable verbose output [default = disabled]\n" 106 "\t-b : begin recording at [+]dd:hh24:mi.ss\n" 107 "\t-e : stop recording at [+]dd:hh24:mi.ss\n" 108 "\t-M : maximum size of destination file\n" 109 "\t-p : pidfile for this process [MUST be specified if daemon]\n" 110 "\t-B : buffer size for inbound (multicast) data [default = %ld bytes]\n" 111 "\t-R : maximum messages to store in buffer (-1 = all) " 112 "[default = -1]\n", 113 (long)DEFAULT_CACHE_LEN ); 114 (void)fprintf(fp, 115 "\t-T : do NOT run as a daemon [default = daemon if root]\n" 116 "\t-n : nice value increment [default = %d]\n" 117 "\t-m : name or address of multicast interface to read from\n" 118 "\t-c : multicast channel to record - ipv4addr:port\n" 119 "\t-l : write output into the logfile\n" 120 "\t-u : seconds to wait before updating on how long till recording starts\n", 121 g_recopt.nice_incr ); 122 123 (void) fprintf( fp, "Examples:\n" 124 " %s -b 15:45.00 -e +2:00.00 -M 1.5Gb -n 2 -B 64K -c 224.0.11.31:5050 " 125 " /opt/video/tv5.mpg \n" 126 "\tbegin recording multicast channel 224.0.11.31:5050 at 15:45 today,\n" 127 "\tfinish recording in two hours or if destination file size >= 1.5 Gb;\n" 128 "\tset socket buffer to 64Kb; increment nice value by 2;\n" 129 "\twrite captured video to /opt/video/tv5.mpg\n", 130 g_udpxrec_app ); 131 (void) fprintf( fp, "\n %s\n", UDPXY_COPYRIGHT_NOTICE ); 132 (void) fprintf( fp, " %s\n\n", UDPXY_CONTACT ); 133 return; 134 } 135 136 137 /* update wait status 138 * 139 */ 140 static void 141 update_waitstat( FILE* log, time_t end_time ) 142 { 143 /* TODO: make sure to distinguish if log is a terminal 144 * terminal gets the progress bar, etc. */ 145 146 double tdiff = difftime( end_time, time(NULL) ); 147 (void) tmfprintf( log, "%.0f\tseconds till recording begins\n", 148 tdiff ); 149 } 150 151 152 /* wait till the given time, update wait status 153 * every update_sec seconds 154 * 155 */ 156 static int 157 wait_till( time_t endtime, int update_sec ) 158 { 159 u_int sec2wait = 0; 160 u_int unslept = 0; 161 time_t now = time(NULL); 162 sig_atomic_t quit = 0; 163 164 TRACE( (void)tmfprintf( g_flog, "%s: waiting till time=[%ld], now=[%ld]\n", 165 __func__, (long)endtime, (long)now ) ); 166 167 if( now >= endtime ) return 0; 168 169 (void) tmfprintf( g_flog, "[%ld] seconds before recording begins\n", 170 (long)(endtime - now) ); 171 172 if( (update_sec <= 0) || 173 ((now + update_sec) > endtime) ) { 174 unslept = sleep( endtime - now ); 175 } 176 else { 177 while( !(quit = must_quit()) && (now < endtime) ) { 178 sec2wait = (now + update_sec) <= endtime 179 ? update_sec : (endtime - now); 180 update_waitstat( g_flog, endtime ); 181 182 TRACE( (void)tmfprintf( g_flog, "Waiting for [%u] more seconds.\n", 183 sec2wait ) ); 184 185 unslept = sleep( sec2wait ); 186 now = time(NULL); 187 } 188 } 189 190 TRACE( (void)tmfprintf( g_flog, "[%u] seconds unslept, quit=[%d]\n", unslept, 191 (long)quit ) ); 192 return (unslept ? ERR_INTERNAL : 0); 193 } 194 195 196 static int 197 calc_buf_settings( ssize_t* bufmsgs, size_t* sock_buflen ) 198 { 199 ssize_t nmsgs = -1, max_buf_used = -1, env_snd_buflen = -1; 200 size_t buflen = 0; 201 202 /* how many messages should we process? */ 203 nmsgs = (g_recopt.rbuf_msgs > 0) ? g_recopt.rbuf_msgs : 204 (int)g_recopt.bufsize / ETHERNET_MTU; 205 206 /* how many bytes could be written at once 207 * to the send socket */ 208 max_buf_used = (g_recopt.rbuf_msgs > 0) 209 ? (ssize_t)(nmsgs * ETHERNET_MTU) : g_recopt.bufsize; 210 if (max_buf_used > g_recopt.bufsize) { 211 max_buf_used = g_recopt.bufsize; 212 } 213 214 assert( max_buf_used >= 0 ); 215 216 env_snd_buflen = get_sizeval( "UDPXREC_SOCKBUF_LEN", 0); 217 buflen = (env_snd_buflen > 0) ? (size_t)env_snd_buflen : (size_t)max_buf_used; 218 219 if (buflen < (size_t) MIN_SOCKBUF_LEN) { 220 buflen = (size_t) MIN_SOCKBUF_LEN; 221 } 222 223 /* cannot go below the size of effective usage */ 224 if( buflen < (size_t)max_buf_used ) { 225 buflen = (size_t)max_buf_used; 226 } 227 228 if (bufmsgs) *bufmsgs = nmsgs; 229 if (sock_buflen) *sock_buflen = buflen; 230 231 TRACE( (void)tmfprintf( g_flog, 232 "min socket buffer = [%ld], " 233 "max space to use = [%ld], " 234 "Rmsgs = [%ld]\n", 235 (long)buflen, (long)max_buf_used, (long)nmsgs ) ); 236 237 return 0; 238 } 239 240 241 242 /* subscribe to the (configured) multicast channel 243 */ 244 static int 245 subscribe( int* sockfd, struct in_addr* mcast_inaddr ) 246 { 247 struct sockaddr_in sa; 248 const char* ipaddr = g_recopt.rec_channel; 249 size_t rcvbuf_len = 0; 250 int rc = 0; 251 252 assert( sockfd && mcast_inaddr ); 253 254 if( 1 != inet_aton( ipaddr, &sa.sin_addr ) ) { 255 mperror( g_flog, errno, 256 "%s: Invalid subscription [%s:%d]: inet_aton", 257 __func__, ipaddr, g_recopt.rec_port ); 258 return -1; 259 } 260 261 sa.sin_family = AF_INET; 262 sa.sin_port = htons( (uint16_t)g_recopt.rec_port ); 263 264 if( 1 != inet_aton( g_recopt.mcast_addr, mcast_inaddr ) ) { 265 mperror( g_flog, errno, 266 "%s: Invalid multicast interface: [%s]: inet_aton", 267 __func__, g_recopt.mcast_addr ); 268 return -1; 269 } 270 271 rc = calc_buf_settings( NULL, &rcvbuf_len ); 272 if (0 != rc) return rc; 273 274 return setup_mcast_listener( &sa, mcast_inaddr, 275 sockfd, (g_recopt.nosync_sbuf ? 0 : rcvbuf_len) ); 276 } 277 278 279 /* record network stream as per spec in opt 280 */ 281 static int 282 record() 283 { 284 int rsock = -1, destfd = -1, rc = 0, wtime_sec = 0; 285 struct in_addr raddr; 286 struct timeval rtv; 287 struct dstream_ctx ds; 288 ssize_t nmsgs = 0; 289 ssize_t nrcv = -1, lrcv = -1, t_delta = 0; 290 uint64_t n_total = 0; 291 ssize_t nwr = -1, lwr = -1; 292 sig_atomic_t quit = 0; 293 struct rdata_opt ropt; 294 int oflags = 0; 295 296 char* data = NULL; 297 298 static const u_short RSOCK_TIMEOUT = 5; 299 extern const char CMD_UDP[]; 300 301 /* NOPs to eliminate warnings in lean version */ 302 (void)&t_delta; (void)&lrcv; 303 t_delta = lrcv = lwr = 0; quit=0; 304 305 check_fragments( NULL, 0, 0, 0, 0, g_flog ); 306 307 /* init */ 308 do { 309 data = malloc( g_recopt.bufsize ); 310 if( NULL == data ) { 311 mperror(g_flog, errno, "%s: cannot allocate [%ld] bytes", 312 __func__, (long)g_recopt.bufsize ); 313 rc = ERR_INTERNAL; 314 break; 315 } 316 317 rc = subscribe( &rsock, &raddr ); 318 if( 0 != rc ) break; 319 320 rtv.tv_sec = RSOCK_TIMEOUT; 321 rtv.tv_usec = 0; 322 323 rc = setsockopt( rsock, SOL_SOCKET, SO_RCVTIMEO, &rtv, sizeof(rtv) ); 324 if( -1 == rc ) { 325 mperror(g_flog, errno, "%s: setsockopt - SO_RCVTIMEO", 326 __func__); 327 rc = ERR_INTERNAL; 328 break; 329 } 330 331 oflags = O_CREAT | O_TRUNC | O_WRONLY | 332 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; 333 # if defined(O_LARGEFILE) 334 /* O_LARGEFILE is not defined under FreeBSD ??-7.1 */ 335 oflags |= O_LARGEFILE; 336 # endif 337 destfd = open( g_recopt.dstfile, oflags, 338 (mode_t)(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); 339 if( -1 == destfd ) { 340 mperror( g_flog, errno, "%s: cannot create destination file [%s]", 341 __func__, g_recopt.dstfile ); 342 rc = ERR_INTERNAL; 343 break; 344 } 345 346 rc = calc_buf_settings( &nmsgs, NULL ); 347 if (0 != rc) return -1; 348 349 if( nmsgs < (ssize_t)1 ) { 350 (void) tmfprintf( g_flog, "Buffer for inbound data is too small [%ld] bytes; " 351 "the minimum size is [%ld] bytes\n", 352 (long)g_recopt.bufsize, (long)ETHERNET_MTU ); 353 rc = ERR_PARAM; 354 break; 355 } 356 357 TRACE( (void)tmfprintf( g_flog, "Inbound buffer set to " 358 "[%d] messages\n", nmsgs ) ); 359 360 rc = init_dstream_ctx( &ds, CMD_UDP, NULL, nmsgs ); 361 if( 0 != rc ) return -1; 362 363 (void) set_nice( g_recopt.nice_incr, g_flog ); 364 365 /* set up alarm to break main loop */ 366 if( 0 != g_recopt.end_time ) { 367 wtime_sec = (int)difftime( g_recopt.end_time, time(NULL) ); 368 assert( wtime_sec >= 0 ); 369 370 (void) alarm( wtime_sec ); 371 372 (void)tmfprintf( g_flog, "Recording will end in [%d] seconds\n", 373 wtime_sec ); 374 } 375 } while(0); 376 377 /* record loop */ 378 ropt.max_frgs = g_recopt.rbuf_msgs; 379 ropt.buf_tmout = -1; 380 381 for( n_total = 0; (0 == rc) && !(quit = must_quit()); ) { 382 nrcv = read_data( &ds, rsock, data, g_recopt.bufsize, &ropt ); 383 if( -1 == nrcv ) { rc = ERR_INTERNAL; break; } 384 385 if( 0 == n_total ) { 386 (void) tmfprintf( g_flog, "Recording to file=[%s] started.\n", 387 g_recopt.dstfile ); 388 } 389 390 TRACE( check_fragments( "received new", g_recopt.bufsize, 391 lrcv, nrcv, t_delta, g_flog ) ); 392 lrcv = nrcv; 393 394 if( nrcv > 0 ) { 395 if( g_recopt.max_fsize && 396 ((int64_t)(n_total + nrcv) >= g_recopt.max_fsize) ) { 397 break; 398 } 399 400 nwr = write_data( &ds, data, nrcv, destfd ); 401 if( -1 == nwr ) { rc = ERR_INTERNAL; break; } 402 403 n_total += (size_t)nwr; 404 /* 405 TRACE( tmfprintf( g_flog, "Wrote [%ld] to file, total=[%ld]\n", 406 (long)nwr, (long)n_total ) ); 407 */ 408 409 TRACE( check_fragments( "wrote to file", 410 nrcv, lwr, nwr, t_delta, g_flog ) ); 411 lwr = nwr; 412 } 413 414 if( ds.flags & F_SCATTERED ) reset_pkt_registry( &ds ); 415 416 } /* record loop */ 417 418 (void) tmfprintf( g_flog, "Recording to file=[%s] stopped at filesize=[%lu] bytes\n", 419 g_recopt.dstfile, (u_long)n_total ); 420 421 /* CLEANUP 422 */ 423 (void) alarm(0); 424 425 TRACE( (void)tmfprintf( g_flog, "Exited record loop: wrote [%lu] bytes to file [%s], " 426 "rc=[%d], alarm=[%ld], quit=[%ld]\n", 427 (u_long)n_total, g_recopt.dstfile, rc, g_alarm, (long)quit ) ); 428 429 free_dstream_ctx( &ds ); 430 if( data ) free( data ); 431 432 close_mcast_listener( rsock, &raddr ); 433 if( destfd >= 0 ) (void) close( destfd ); 434 435 if( quit ) 436 TRACE( (void)tmfprintf( g_flog, "%s process must quit\n", 437 g_udpxrec_app ) ); 438 439 return rc; 440 } 441 442 443 /* make sure the channel is valid: subscribe/close 444 */ 445 static int 446 verify_channel() 447 { 448 struct in_addr mcast_inaddr; 449 int sockfd = -1, rc = -1; 450 char buf[16]; 451 ssize_t nrd = -1; 452 struct timeval rtv; 453 454 static const time_t MSOCK_TMOUT_SEC = 2; 455 456 rc = subscribe( &sockfd, &mcast_inaddr ); 457 do { 458 if( rc ) break; 459 460 rtv.tv_sec = MSOCK_TMOUT_SEC; 461 rtv.tv_usec = 0; 462 rc = setsockopt( sockfd, SOL_SOCKET, SO_RCVTIMEO, &rtv, sizeof(rtv) ); 463 if( -1 == rc ) { 464 mperror(g_flog, errno, "%s: setsockopt - SO_RCVTIMEO", 465 __func__); 466 rc = ERR_INTERNAL; break; 467 } 468 469 /* attempt to read from the socket to 470 * make sure the channel is alive 471 */ 472 nrd = read( sockfd, buf, sizeof(buf) ); 473 if( nrd <= 0 ) { 474 rc = errno; 475 mperror( g_flog, errno, "channel read" ); 476 if( EAGAIN == rc ) { 477 (void) tmfprintf( g_flog, 478 "failed to read from [%s:%d]\n", 479 g_recopt.rec_channel, g_recopt.rec_port ); 480 } 481 rc = ERR_INTERNAL; break; 482 } 483 484 TRACE( (void)tmfprintf( g_flog, "%s: read [%ld] bytes " 485 "from source channel\n", __func__, nrd ) ); 486 } while(0); 487 488 if( sockfd >= 0 ) { 489 close_mcast_listener( sockfd, &mcast_inaddr ); 490 } 491 492 return rc; 493 } 494 495 496 /* set up signal handling 497 */ 498 static int 499 setup_signals() 500 { 501 struct sigaction qact, oldact; 502 503 qact.sa_handler = handle_quitsigs; 504 sigemptyset(&qact.sa_mask); 505 qact.sa_flags = 0; 506 507 if( (sigaction(SIGTERM, &qact, &oldact) < 0) || 508 (sigaction(SIGQUIT, &qact, &oldact) < 0) || 509 (sigaction(SIGINT, &qact, &oldact) < 0) || 510 (sigaction(SIGALRM, &qact, &oldact) < 0) || 511 (sigaction(SIGPIPE, &qact, &oldact) < 0)) { 512 perror("sigaction-quit"); 513 514 return ERR_INTERNAL; 515 } 516 517 return 0; 518 } 519 520 521 extern int udpxrec_main( int argc, char* const argv[] ); 522 523 /* main() for udpxrec module 524 */ 525 int udpxrec_main( int argc, char* const argv[] ) 526 { 527 int rc = 0, ch = 0, custom_log = 0, no_daemon = 0; 528 static const char OPTMASK[] = "vb:e:M:p:B:n:m:l:c:R:u:T"; 529 time_t now = time(NULL); 530 char now_buf[ 32 ] = {0}, sel_buf[ 32 ] = {0}, app_finfo[80] = {0}; 531 532 extern int optind, optopt; 533 extern const char IPv4_ALL[]; 534 535 mk_app_info(g_udpxrec_app, g_app_info, sizeof(g_app_info) - 1); 536 537 if( argc < 2 ) { 538 usage( argv[0], stderr ); 539 return ERR_PARAM; 540 } 541 542 rc = init_recopt( &g_recopt ); 543 while( (0 == rc) && (-1 != (ch = getopt( argc, argv, OPTMASK ))) ) { 544 switch(ch) { 545 case 'T': no_daemon = 1; break; 546 case 'v': set_verbose( &g_recopt.is_verbose ); break; 547 case 'b': 548 if( (time_t)0 != g_recopt.end_time ) { 549 (void) fprintf( stderr, "Cannot specify start-recording " 550 "time after end-recording time has been set\n" ); 551 } 552 553 rc = a2time( optarg, &g_recopt.bg_time, time(NULL) ); 554 if( 0 != rc ) { 555 (void) fprintf( stderr, "Invalid time: [%s]\n", optarg ); 556 rc = ERR_PARAM; 557 } 558 else { 559 if( g_recopt.bg_time < now ) { 560 (void)strncpy( now_buf, Zasctime(localtime( &now )), 561 sizeof(now_buf) ); 562 (void)strncpy( sel_buf, 563 Zasctime(localtime( &g_recopt.bg_time )), 564 sizeof(sel_buf) ); 565 566 (void) fprintf( stderr, 567 "Selected %s time is in the past, " 568 "now=[%s], selected=[%s]\n", "start", 569 now_buf, sel_buf ); 570 rc = ERR_PARAM; 571 } 572 } 573 574 break; 575 case 'e': 576 if( (time_t)0 == g_recopt.bg_time ) { 577 g_recopt.bg_time = time(NULL); 578 (void)fprintf( stderr, 579 "Start-recording time defaults to now [%s]\n", 580 Zasctime( localtime( &g_recopt.bg_time ) ) ); 581 } 582 583 rc = a2time( optarg, &g_recopt.end_time, g_recopt.bg_time ); 584 if( 0 != rc ) { 585 (void) fprintf( stderr, "Invalid time: [%s]\n", optarg ); 586 rc = ERR_PARAM; 587 } 588 else { 589 if( g_recopt.end_time < now ) { 590 (void)strncpy( now_buf, Zasctime(localtime( &now )), 591 sizeof(now_buf) ); 592 (void)strncpy( sel_buf, 593 Zasctime(localtime( &g_recopt.end_time )), 594 sizeof(sel_buf) ); 595 596 (void) fprintf( stderr, 597 "Selected %s time is in the past, " 598 "now=[%s], selected=[%s]\n", "end", 599 now_buf, sel_buf ); 600 rc = ERR_PARAM; 601 } 602 } 603 break; 604 605 case 'M': 606 rc = a2int64( optarg, &g_recopt.max_fsize ); 607 if( 0 != rc ) { 608 (void) fprintf( stderr, "Invalid file size: [%s]\n", 609 optarg ); 610 rc = ERR_PARAM; 611 } 612 break; 613 case 'p': 614 g_recopt.pidfile = strdup(optarg); 615 break; 616 617 case 'B': 618 rc = a2size( optarg, &g_recopt.bufsize ); 619 if( 0 != rc ) { 620 (void) fprintf( stderr, "Invalid buffer size: [%s]\n", 621 optarg ); 622 rc = ERR_PARAM; 623 } 624 else if( (g_recopt.bufsize < MIN_MCACHE_LEN) || 625 (g_recopt.bufsize > MAX_MCACHE_LEN)) { 626 (void) fprintf( stderr, 627 "Buffer size must be in [%ld-%ld] bytes range\n", 628 (long)MIN_MCACHE_LEN, (long)MAX_MCACHE_LEN ); 629 rc = ERR_PARAM; 630 } 631 632 break; 633 case 'n': 634 g_recopt.nice_incr = atoi( optarg ); 635 if( 0 == g_recopt.nice_incr ) { 636 (void) fprintf( stderr, 637 "Invalid nice-value increment: [%s]\n", optarg ); 638 rc = ERR_PARAM; 639 } 640 break; 641 case 'm': 642 rc = get_ipv4_address( optarg, g_recopt.mcast_addr, 643 sizeof(g_recopt.mcast_addr) ); 644 if( 0 != rc ) { 645 (void) fprintf( stderr, "Invalid multicast address: [%s]\n", 646 optarg ); 647 rc = ERR_PARAM; 648 } 649 break; 650 case 'l': 651 g_flog = fopen( optarg, "a" ); 652 if( NULL == g_flog ) { 653 rc = errno; 654 (void) fprintf( stderr, "Error opening logfile [%s]: %s\n", 655 optarg, strerror(rc) ); 656 rc = ERR_PARAM; break; 657 } 658 659 Setlinebuf( g_flog ); 660 custom_log = 1; 661 break; 662 663 case 'c': 664 rc = get_addrport( optarg, g_recopt.rec_channel, 665 sizeof( g_recopt.rec_channel ), 666 &g_recopt.rec_port ); 667 if( 0 != rc ) rc = ERR_PARAM; 668 break; 669 670 case 'R': 671 g_recopt.rbuf_msgs = atoi( optarg ); 672 if( (g_recopt.rbuf_msgs <= 0) && (-1 != g_recopt.rbuf_msgs) ) { 673 (void) fprintf( stderr, 674 "Invalid rcache size: [%s]\n", optarg ); 675 rc = ERR_PARAM; 676 } 677 break; 678 679 case 'u': 680 g_recopt.waitupd_sec = atoi(optarg); 681 if( g_recopt.waitupd_sec <= 0 ) { 682 (void) fprintf( stderr, "Invalid wait-update value [%s] " 683 "(must be a number > 0)\n", optarg ); 684 rc = ERR_PARAM; 685 } 686 break; 687 688 case ':': 689 (void) fprintf( stderr, "Option [-%c] requires an argument\n", 690 optopt ); 691 rc = ERR_PARAM; break; 692 case '?': 693 (void) fprintf( stderr, "Unrecognized option: [-%c]\n", optopt ); 694 rc = ERR_PARAM; break; 695 default: 696 usage( argv[0], stderr ); 697 rc = ERR_PARAM; break; 698 699 } /* switch */ 700 } /* while getopt */ 701 702 if( 0 == rc ) { 703 if( optind >= argc ) { 704 (void) fputs( "Missing destination file parameter\n", stderr ); 705 rc = ERR_PARAM; 706 } 707 else { 708 g_recopt.dstfile = strdup( argv[optind] ); 709 } 710 711 if( !(g_recopt.max_fsize > 0 || g_recopt.end_time) ) { 712 (void) fputs( "Must specify either max file [-M] size " 713 "or end time [-e]\n", stderr ); 714 rc = ERR_PARAM; 715 } 716 717 if( !g_recopt.rec_channel[0] || !g_recopt.rec_port ) { 718 (void) fputs( "Must specify multicast channel to record from\n", 719 stderr ); 720 rc = ERR_PARAM; 721 } 722 } 723 724 if( rc ) { 725 free_recopt( &g_recopt ); 726 return rc; 727 } 728 729 do { 730 if( '\0' == g_recopt.mcast_addr[0] ) { 731 (void) strncpy( g_recopt.mcast_addr, IPv4_ALL, 732 sizeof(g_recopt.mcast_addr) - 1 ); 733 } 734 735 if( !custom_log ) { 736 /* in debug mode output goes to stderr, otherwise to /dev/null */ 737 g_flog = ((uf_TRUE == g_recopt.is_verbose) 738 ? stderr 739 : fopen( "/dev/null", "a" )); 740 if( NULL == g_flog ) { 741 perror("fopen"); 742 rc = ERR_INTERNAL; break; 743 } 744 } 745 746 if( 0 == geteuid() ) { 747 if( !no_daemon ) { 748 if( stderr == g_flog ) { 749 (void) fprintf( stderr, 750 "Logfile must be specified to run " 751 "in verbose mode in background\n" ); 752 rc = ERR_PARAM; break; 753 } 754 755 if( NULL == g_recopt.pidfile ) { 756 (void) fprintf( stderr, "pidfile must be specified " 757 "to run as daemon\n" ); 758 rc = ERR_PARAM; break; 759 } 760 761 if( 0 != (rc = daemonize(0, g_flog)) ) { 762 rc = ERR_INTERNAL; break; 763 } 764 } 765 766 } /* 0 == geteuid() */ 767 768 if( NULL != g_recopt.pidfile ) { 769 rc = make_pidfile( g_recopt.pidfile, getpid(), g_flog ); 770 if( 0 != rc ) break; 771 } 772 773 (void) set_nice( g_recopt.nice_incr, g_flog ); 774 775 if( 0 != (rc = setup_signals()) ) break; 776 777 TRACE( fprint_recopt( g_flog, &g_recopt ) ); 778 779 TRACE( printcmdln( g_flog, g_app_info, argc, argv ) ); 780 781 if( g_recopt.bg_time ) { 782 if( 0 != (rc = verify_channel()) || g_quit ) 783 break; 784 785 rc = wait_till( g_recopt.bg_time, g_recopt.waitupd_sec ); 786 if( rc || g_quit ) break; 787 } 788 789 rc = record(); 790 791 if( NULL != g_recopt.pidfile ) { 792 if( -1 == unlink(g_recopt.pidfile) ) { 793 mperror( g_flog, errno, "unlink [%s]", g_recopt.pidfile ); 794 } 795 } 796 } 797 while(0); 798 799 if( g_flog ) { 800 (void)tmfprintf( g_flog, "%s is exiting with rc=[%d]\n", 801 app_finfo, rc ); 802 } 803 804 if( g_flog && (stderr != g_flog) ) { 805 (void) fclose(g_flog); 806 } 807 808 free_recopt( &g_recopt ); 809 810 return rc; 811 } 812 813 814 /* __EOF__ */ 815 816