Exemplo n.º 1
0
Arquivo: main.c Projeto: l3ib/fsniper
/* frees memory.  called from more than one place because fork'd copies
 * still have globals hanging around before they are exited.
 *
 * frees:
 * - config
 * - configfile
 * - watchnode elements
 * - pipe list
 *
 * called from:
 * - handle_quit_signal (in case of main exiting) 
 * - exit_handler (in case of handler exiting)
 */
void free_all_globals()
{
    struct pipe_list* tmp_pipe;

    /* free config here */ 
    keyval_node_free_all(config);

    /* free watchnode elements */
    free_watchnodes();
    free(g_watchnode);

    /* free / close any remaining pipes in the list */    
    if (pipe_list_head)
    {
        tmp_pipe = pipe_list_head->next;
        while(tmp_pipe)
            tmp_pipe = pipe_list_remove(pipe_list_head, tmp_pipe);
        free(pipe_list_head);
    }
   
    free(configfile);
}
Exemplo n.º 2
0
Arquivo: main.c Projeto: l3ib/fsniper
int main(int argc, char** argv)
{
    int ifd, len = 0, i = 0, selectret = 0, maxfd, retryselect, pid;
    char buf[BUF_LEN]; 
    char *configdir;
    char *pidfilename;
    char *statusfilename;
    char *statusbin;
    char *error_str;
    char *version_str = PACKAGE_STRING;
    char *pbuf;
    char *filename;
    FILE *pidfile;
    FILE *statusfile;
    fd_set set;
    struct inotify_event *event;
    struct argument *argument = argument_new();
    struct pipe_list *pipe_list_cur;
    struct stat file_stat;
    struct watchnode *node;

    /* alloc pipe list */
    pipe_list_head = malloc(sizeof(struct pipe_list));
    pipe_list_head->next = NULL;

    /* set up signals for exiting/reaping */ 
    signal(SIGINT, &handle_quit_signal); 
    signal(SIGTERM, &handle_quit_signal);
    signal(SIGCHLD, &handle_child_signal);
    signal(SIGHUP, &handle_hup_signal);


    /* add command line arguments */
    argument_register(argument, "help", "Prints this help text.", 0);
    argument_register(argument, "version", "Prints version information.", 0);
    argument_register(argument, "daemon", "Run as a daemon.", 0);
    argument_register(argument, "verbose", "Turns on debug text.", 0);
    argument_register(argument, "sync", "Sync mode (for debugging).", 0);
    argument_register(argument, "log-to-stdout", "Deprecated, use \"--log-to=stdout\" instead", 0);
    argument_register(argument, "log-to", "Log messages with specified way. "
#ifdef USE_SYSLOG
                                "Can be: stdout, file, syslog. \"file\" by default.", 1);
#else
                                "Can be: stdout, file. \"file\" by default.", 1);
#endif

    if ((error_str = argument_parse(argument, argc, argv))) {
	fprintf(stderr, "Error in arguments: %s", error_str);
	free(error_str);
	return -1;
    }

    if (argument_exists(argument, "help")) {
	char *help_txt = argument_get_help_text(argument);
	printf("%s", help_txt);
	free(help_txt);
	return 0;
    }

    if (argument_exists(argument, "version")) {
	printf("%s\n", version_str);
	return 0;
    }

    if (argument_exists(argument, "verbose")) {
	verbose = 1;
    }

    if (argument_exists(argument, "daemon") && fork())
	return 0;

    if (argument_exists(argument, "sync"))
	syncmode = 1;


    if (argument_exists(argument, "log-to-stdout"))
        fprintf(stderr, "Warning, this option is deprecated, " \
                        "please use new syntax: \"--log-to=stdout\".\n");

    logtype = LOG_FILE;
    if (argument_exists(argument, "log-to") && \
        (log_arg = argument_get_value(argument, "log-to")) != NULL)
    {
        if      (strcmp(log_arg, "stdout") == 0)
            logtype = LOG_STDOUT;
#ifdef USE_SYSLOG
        else if (strcmp(log_arg, "syslog") == 0)
            logtype = LOG_SYS;
#endif
        else /* logtype already set to 'file' above */
            fprintf(stderr, "Warning, selected unknown logging type. " \
                            "Will use \"--log-to=file\" instead.\n");
    }

    /* get config dir (must free this) */
    configdir = get_config_dir();	

    /* if a config file has not been specified, use default */
    if (argument_get_extra(argument))
    {
	configfile = strdup(argument_get_extra(argument));
    }
    else
    {
	configfile = malloc (strlen(configdir) + strlen ("/config") + 1);
	sprintf(configfile, "%s/config", configdir);
    }

    argument_free(argument);
    free(configdir);

    if (access(configfile, R_OK) != 0)
    {
	fprintf(stderr, "error: could not open config file: %s\n", configfile);
	return -1;
    }

    /* create a pid file */
    pidfilename = get_pid_filename();
	
    if (stat(pidfilename, &file_stat) == 0) /* pidfile exists */
    {
	pidfile = fopen(pidfilename, "r");
		
	if (fscanf(pidfile, "%d", &pid) == 1) /* pidfile has a pid inside */
	{
	    char *binaryname;
	    char *scanformat; 
	    if ((binaryname = strrchr(argv[0], '/')) != NULL)
	    {
		binaryname++;
	    }
	    else
	    {
		binaryname = argv[0];
	    }

	    scanformat = malloc(strlen("Name:   %") + strlen(binaryname) + strlen("s") + 1);
	    statusfilename = malloc(strlen("/proc/") + 6 + strlen("/status") + 1);
	    sprintf(statusfilename, "/proc/%d/status", pid);

	    if (stat(statusfilename, &file_stat) != 0) /* write pid file if the process no longer exists */
	    {
		write_pid_file(pidfilename);
	    }
	    else /* process exists, so check owner and binary name */
	    {
		statusfile = fopen(statusfilename, "r");
		statusbin = malloc(strlen(binaryname) + 2); /* the binary name may start with "fsniper" but be longer */
		sprintf(scanformat, "Name:   %%%ds", strlen(binaryname) + 1);
		fscanf(statusfile, scanformat, statusbin);
		free(statusfilename);
		fclose(statusfile);
		fclose(pidfile);
				
		if (strcmp(binaryname, statusbin) == 0 && file_stat.st_uid == getuid())
		    /* exit if the process is fsniper and is owned by the current user */
		{
		    printf("%s: already running instance found with pid %d. exiting.\n", binaryname, pid);
		    exit(1);
		}
		else /* the pid file contains an old pid, one that isn't fsniper, or one not owned by the current user */
		{
		    write_pid_file(pidfilename);
		}
	    }
	}
	else /* pidfile is invalid */
	{
	    fclose(pidfile);
	    write_pid_file(pidfilename);
	}
    }
    else /* the pidfile doesn't exist */
    {
	write_pid_file(pidfilename);
    }
    free(pidfilename);

    /* start up log */
    if (!log_open())
    {
	fprintf(stderr, "Error: could not start log.\n");
	return -1;
    }

    ifd = inotify_init();
    if (ifd < 0)
    {
	perror("inotify_init");
	return -1;
    }

    if (verbose) log_write("Parsing config file: %s\n", configfile);
    config = keyval_parse_file(configfile);

    if ((error_str = keyval_get_error())) {
        fprintf(stderr, "%s", error_str);
        free(error_str);
        exit(1);
    }

    validate_config(config);

    /* add nodes to the inotify descriptor */
    g_watchnode = add_watches(ifd);

    /* wait for events and then handle them */
    while (1)
    {		
	/* set up fds and max */
	FD_ZERO(&set);
	FD_SET(ifd, &set);
	maxfd = ifd;
	for (pipe_list_cur = pipe_list_head->next; pipe_list_cur; pipe_list_cur = pipe_list_cur->next)
	{
	    FD_SET(pipe_list_cur->pfd[0], &set);
	    if (pipe_list_cur->pfd[0] > maxfd)
		maxfd = pipe_list_cur->pfd[0];
	}

	retryselect = 1;
	while (retryselect)
	{
	    /* use select to get activity on any of the fds */
	    selectret = select(maxfd + 1, &set, NULL, NULL, NULL);

	    if (selectret == -1)
	    {
		if (errno == EINTR)
		    retryselect = 1;
		else
		    handle_quit_signal(-2);
	    } else
		retryselect = 0;
	}
		
	/* handle any events on the inotify fd */
	if (FD_ISSET(ifd, &set))
	{
	    len = read(ifd, buf, BUF_LEN);
	    while (i < len)
	    {
		event = (struct inotify_event *) &buf[i];
		if (event->len && (event->mask & IN_CLOSE_WRITE || event->mask & IN_MOVED_TO))
		{
		    /* if sync mode, just call handle_exec */
		    if (syncmode == 1)
		    {
			handle_event(event, fileno(_logfd));
		    }
		    else
		    {
			/* create new pipe_list entry */
			for (pipe_list_cur = pipe_list_head; pipe_list_cur->next != NULL; pipe_list_cur = pipe_list_cur->next) {}

			pipe_list_cur->next = malloc(sizeof(struct pipe_list));
			pipe_list_cur->next->next = NULL;

			/* create pipe */
			pipe(pipe_list_cur->next->pfd);

			if (fork() == 0) 
			{
			    /* child, close 0 */
			    close(pipe_list_cur->next->pfd[0]);					
			    log_close();
			    signal(SIGINT, &handle_child_quit_signal);
			    signal(SIGTERM, &handle_child_quit_signal);
			    handle_event(event, pipe_list_cur->next->pfd[1]);
			} else {
			    /* parent, close 1 */
			    close(pipe_list_cur->next->pfd[1]);
			}
		    }
		}
                else if (event->len && (event->mask & IN_CREATE && event->mask & IN_ISDIR))
                {
                    for (node = g_watchnode->next; node; node = node->next)
                        if (node->wd == event->wd)
                            break;

                    if (node)
                    {
                        /* combine the name inotify gives with the full path to the file */
                        filename = malloc(strlen(node->path) + strlen("/") + strlen(event->name) + 1);
                        sprintf(filename, "%s/%s", node->path, event->name);
                        watch_dir(node, ifd, strdup(filename), node->section);
                        free(filename);
                    }
                }
		else if (event->len && (event->mask & IN_DELETE && event->mask & IN_ISDIR))
                {
                    for (node = g_watchnode->next; node; node = node->next)
                        if (node->wd == event->wd)
                            break;

                    if (node)
                    {
                        /* combine the name inotify gives with the full path to the file */
                        filename = malloc(strlen(node->path) + strlen("/") + strlen(event->name) + 1);
                        sprintf(filename, "%s/%s", node->path, event->name);
                        unwatch_dir(filename, ifd);
                        free(filename);
                    }
                }
                i += EVENT_SIZE + event->len;
            }
	    i = 0;
	}
		
	/* now lets see if we have any pipe activity */
	pipe_list_cur = pipe_list_head->next;
	while (pipe_list_cur)
	{
	    if (FD_ISSET(pipe_list_cur->pfd[0], &set))
	    {
		len = read(pipe_list_cur->pfd[0], buf, BUF_LEN);
		if (len == 0)
		{
		    close(pipe_list_cur->pfd[0]);
		    /* remove this item from the list */
		    pipe_list_cur = pipe_list_remove(pipe_list_head, pipe_list_cur);
					
		} else {
		    /* print it somewhere */
		    pbuf = malloc(len + 1);
		    snprintf(pbuf, len, "%s", buf);
		    log_write("%s\n", pbuf);
		    free(pbuf);
		    pipe_list_cur = pipe_list_cur->next;
		}
	    } else {
		pipe_list_cur = pipe_list_cur->next;

	    }


	}
    }
}
Exemplo n.º 3
0
Arquivo: teepot.c Projeto: dkj/teepot
static ssize_t do_write(Output *output, ChunkList *chunks,
			Opts *options, SpillControl *spillage, int index) {
  ssize_t bytes = 0;
  Chunk *curr_chunk = output->curr_chunk;

  while (curr_chunk->next != NULL || output->offset < curr_chunk->len) {
    /* While there's something to write ... */

    assert(NULL != curr_chunk->data);

    if (output->offset < curr_chunk->len) {
      /* Data available in the current Chunk */
      ssize_t b;

      /* Send it */
      do {
	b = write(output->fd, curr_chunk->data + output->offset,
		  curr_chunk->len - output->offset);
      } while (b < 0 && EINTR == errno);

      if (b < 0) { /* Error */
	if (EAGAIN == errno || EWOULDBLOCK == errno) break; /* Blocking is OK */
	if (EPIPE == errno) return -2;  /* Got EPIPE, file should be closed */
	fprintf(stderr, "Error writing to %s : %s\n",
		output->name, strerror(errno));
	return -1;
      }

      if (b == 0) break;  /* Wrote nothing, try again later */

      /* Update amount read */
      output->written += b;
      output->offset += b;
      bytes += b;

      /* Record time and update linked list */
      if (!output->is_reg) {
	output->write_time = get_time();
	if (output->write_time < 0) {
	  return -1;
	}
	
	while (NULL != output->next
	       && output->next->written < output->written) {
	  Output *n = output->next;
	  assert(n->prev == output);
	  pipe_list_remove(output, spillage);
	  pipe_list_insert(output, n, spillage);
	}

	if (output == spillage->blocking_output) {
	  spillage->blocking_output = spillage->pipe_list_head;
	}
      }
    }

    assert(output->offset <= curr_chunk->len);

    /* Check if at end of current Chunk */
    if (output->offset == curr_chunk->len) {
      /* Stop sending if no more Chunks yet */
      if (NULL == curr_chunk->next) break;

      /* Otherwise, move on to the next Chunk */
      output->curr_chunk = curr_chunk->next;
      output->offset = 0;

      --curr_chunk->nwriters;
      if (0 != release_chunk(chunks, curr_chunk, options, spillage)) {
	return -1;
      }

      curr_chunk = output->curr_chunk;
      curr_chunk->nwriters++;

      if (NULL == curr_chunk->data) {
	/* Need to re-read spilled data */
	if (0 != reread_data(curr_chunk, spillage)) return -1;
      }
    }
  }

  if (verbosity > 2 && bytes > 0) {
    fprintf(stderr,
	    "%.3f Wrote %zd bytes to output #%d (%s); %lld (%sB) so far.\n",
	    get_time(), bytes, index, output->name,
	    (long long) output->written, human_size(output->written));
  }

  return bytes;
}
Exemplo n.º 4
0
Arquivo: teepot.c Projeto: dkj/teepot
static int do_copy(Opts *options, Input *in,
		   int noutputs, Output *outputs,
		   int nregular, int *regular,
		   int npipes, int *pipes,
		   SpillControl *spillage) {
  ChunkList chunks = { NULL, NULL, NULL }; /* Linked list of Chunks */ 
  struct pollfd *polls; /* structs for poll(2) */
  int   *poll_idx;    /* indexes in outputs corresponding to entries in polls */
  int   *closing_pipes; /* Pipes that need to be closed */
  int   *closing_reg;   /* Regular files that need to be closed */
  int i, keeping_up = npipes, read_eof = 0, nclosed = 0;

  chunks.head = chunks.tail = calloc(1, sizeof(Chunk));  /* Initial Chunk */
  polls         = malloc((noutputs + 1) * sizeof(struct pollfd));
  poll_idx      = malloc((noutputs + 1) * sizeof(int));
  closing_pipes = malloc((npipes + 1)   * sizeof(int));
  closing_reg   = malloc((nregular + 1) * sizeof(int));
  if (NULL == chunks.head || NULL == polls || NULL == poll_idx
      || NULL == closing_pipes || NULL == closing_reg) {
    perror("do_copy");
    return -1;
  }

  chunks.head->ref_count = noutputs;
  chunks.head->nwriters  = noutputs;

  /* Point all outputs to the initial Chunk and build linked list of pipes */
  for (i = 0; i < noutputs; i++) {
    outputs[i].curr_chunk = chunks.head;
    if (!outputs[i].is_reg) pipe_list_push(&outputs[i], spillage);
  }

  do {  /* Main loop */
    int npolls = 0, pipe_close = 0, reg_close = 0;
    /* Are we waiting for a slow output? */
    int blocked = (NULL != spillage->blocking_output
		   && spillage->alloced >= options->max);
    int timeout = -1;
    int should_read;

    if (blocked) {
      /* Work out how long to wait in poll */
      double now = get_time();
      double left;
      if (now < 0) return -1;
      if (now < spillage->blocking_output->write_time) {
	/* Someone fiddled with the clock? */
	spillage->blocking_output->write_time = now;
      }
      left = options->wait_time - (now - spillage->blocking_output->write_time);
      timeout = left > 0 ? (int)(left * 1000) : 0;
      if (timeout == 0) { /* Remove the block */
	blocked = 0;
	spillage->blocking_output = NULL;
      }
    }

    /* Only try to read if not at EOF; either there are no 
       pipes or at least one pipe has nothing left to write;
       and we aren't waiting for a slow output in order to prevent spillage */
    should_read = (!read_eof
		   && (npipes == 0 || keeping_up > 0)
		   && !blocked);

    if (should_read) {
      if (in->reg) {
	/* If reading a regular file, do it now */
	if (0 != do_read(options, in, spillage, &chunks, &read_eof,
			 noutputs - nclosed)) {
	  return -1;
	}
      } else {
	/* Otherwise add it to the poll list */
	polls[npolls].fd = in->fd;
	poll_idx[npolls] = -1;
	polls[npolls].events = POLLIN;
	polls[npolls++].revents = 0;
      }
    }

    keeping_up = 0;  /* Number of pipes that are keeping up */

    /* Add all the pipe outputs that have something to write to the poll list */
    for (i = 0; i < npipes; i++) {
      if (outputs[pipes[i]].curr_chunk != chunks.tail
	  || outputs[pipes[i]].offset < chunks.tail->len) {
	/* Something to write */
	polls[npolls].fd = outputs[pipes[i]].fd;
	poll_idx[npolls] = i;
	polls[npolls].events = POLLOUT|POLLERR|POLLHUP;
	polls[npolls++].revents = 0;
      } else {
	/* Keeping up or finished */
	if (read_eof) {
	  closing_pipes[pipe_close++] = i;
	  timeout = 0; /* Ensure pipe gets closed promptly */
	} else {
	  keeping_up++;
	}
      }
    }
    
    if (npolls > 0) {  /* Need to do some polling */
      int ready;
      do {
	ready = poll(polls, npolls, timeout);
      } while (ready < 0 && EINTR == errno);

      if (ready < 0) {
	perror("poll failed in do_copy");
	return -1;
      }

      for (i = 0; i < npolls && ready > 0; i++) {
	if (polls[i].revents) {  /* Got some events */
	  
	  --ready;
	  if (poll_idx[i] < 0) {  /* Input, try to read from it. */
	    if (0 != do_read(options, in, spillage, &chunks,
			     &read_eof, noutputs - nclosed)) {
	      return -1;
	    }

	  } else {  /* Output, try to write to it. */
	    Output *output = &outputs[pipes[poll_idx[i]]];
	    ssize_t res = do_write(output, &chunks, options,
				   spillage, pipes[poll_idx[i]]);

	    if (-2 == res) { /* Got EPIPE, add to closing_pipes list */
	      closing_pipes[pipe_close++] = poll_idx[i];
	      continue;
	    } else if (res < 0) { /* other write error, give up */
	      return -1;
	    }

	    if (output->curr_chunk == chunks.tail
		&& output->offset == chunks.tail->len) {
	      /* All the data so far has been written to this output */
	      if (read_eof) {
		/* If finished reading, add to closing_pipes */
		closing_pipes[pipe_close++] = poll_idx[i];
	      } else {
		/* otherwise, add to keeping_up count, to
		   encourage more reading */
		keeping_up++;
	      }
	    }
	  }
	}
      }
    } /* End of polling section */

    /* Deal with regular output files */

    for (i = 0; i < nregular; i++) {
      /* Try to write */
      if (do_write(&outputs[regular[i]], &chunks,
		   options, spillage, regular[i]) < 0) {
	return -1;
      }

      if (read_eof
	  && outputs[regular[i]].curr_chunk == chunks.tail
	  && outputs[regular[i]].offset == chunks.tail->len) {
	/* If all data written and finished reading, add to closing_reg list */
	closing_reg[reg_close++] = i;
      }
    }

    /* Close any regular files that have finished */

    for (i = reg_close - 1; i >= 0; i--) {
      int to_close = regular[closing_reg[i]];

      assert(outputs[to_close].fd >= 0);
      if (0 != close(outputs[to_close].fd)) {
	fprintf(stderr, "Error closing %s : %s\n",
		outputs[to_close].name, strerror(errno));
	return -1;
      }
      if (verbosity > 1) {
	fprintf(stderr, "%.3f Closed output #%d (%s)\n",
		get_time(), to_close, outputs[to_close].name);
      }
      outputs[to_close].fd = -1;

      /* Remove the entry from the regular array */
      if (closing_reg[i] < nregular - 1) {
	memmove(&regular[closing_reg[i]], &regular[closing_reg[i] + 1],
		(nregular - closing_reg[i] - 1) * sizeof(regular[0]));
      }

      if (0 != release_chunks(&chunks, outputs[to_close].curr_chunk,
			      options, spillage)) {
	return -1;
      }
      nclosed++;
      nregular--;
    }

    /* Close any poll-able files that have finished */

    for (i = pipe_close - 1; i >= 0; i--) {
      int to_close = pipes[closing_pipes[i]];

      assert(outputs[to_close].fd >= 0);
      if (0 != close(outputs[to_close].fd)) {
	fprintf(stderr, "Error closing %s : %s\n",
		outputs[to_close].name, strerror(errno));
	return -1;
      }
      if (verbosity > 1) {
	fprintf(stderr, "%.3f Closed output #%d (%s)\n",
		get_time(), to_close, outputs[to_close].name);
      }
      outputs[to_close].fd = -1;

      /* Remove the entry from the pipes array */
      if (closing_pipes[i] < npipes - 1) {
	memmove(&pipes[closing_pipes[i]], &pipes[closing_pipes[i] + 1],
		(npipes - closing_pipes[i] - 1) * sizeof(pipes[0]));
      }

      /* Remove from spillage linked list */
      pipe_list_remove(&outputs[to_close], spillage);

      /* Release any data referenced by this output */
      if (0 != release_chunks(&chunks, outputs[to_close].curr_chunk,
			      options, spillage)) {
	return -1;
      }
      nclosed++;
      npipes--;
    }
  } while (nclosed < noutputs);

  if (verbosity > 0) {
    double now = get_time();
    fprintf(stderr, "%.3f Read %lld bytes (%sB) from input (%s)\n",
	    now, (long long) in->pos, human_size(in->pos), options->in_name);
    for (i = 0; i < noutputs; i++) {
      fprintf(stderr, "%.3f Wrote %lld bytes (%sB) to output #%d (%s)\n",
	      now, (long long) outputs[i].written,
	      human_size(outputs[i].written),
	      i, outputs[i].name);
    }
    fprintf(stderr, "%.3f Maximum buffer used = %zd bytes (%sB)\n",
	    now, spillage->max_alloced, human_size(spillage->max_alloced));
    if (!in->reg) {
      fprintf(stderr, "%.3f Spilled %lld bytes (%sB) to temporary files\n",
	      now, (long long) spillage->total_spilled,
	      human_size(spillage->total_spilled));
      if (spillage->total_spilled > 0) {
	fprintf(stderr, "%.3f Maximum spilled at one time = %lld bytes (%sB)\n",
		now, (long long) spillage->max_spilled,
		human_size(spillage->max_spilled));
	fprintf(stderr, "%.3f Total temporary files opened = %d\n",
		now, spillage->spill_file_count);
	fprintf(stderr,
		"%.3f Maximum temporary files in use at one time = %d\n",
		now, spillage->max_spill_files);
      }
    }
  }

  return 0;
}