Changes malloc to zmalloc
[ust.git] / ustd / ustd.c
index 66b9dd382028befb523b1790fe3b2d7f992b29f6..c0f4c596eca0ce9a12165a1d090a3c655db0fcdf 100644 (file)
+/* Copyright (C) 2009  Pierre-Marc Fournier
+ *               2010  Alexis Halle
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
 #define _GNU_SOURCE
 
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <sys/shm.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
 
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <getopt.h>
 
-#include "localerr.h"
-#include "ustcomm.h"
-
-struct list_head buffers = LIST_HEAD_INIT(buffers);
-
-struct buffer_info {
-       char *name;
-       pid_t pid;
+#include "ust/ustd.h"
+#include "usterr.h"
 
-       int shmid;
-       void *mem;
-       int memlen;
+char *sock_path=NULL;
+char *trace_path=NULL;
+int daemon_mode = 0;
+char *pidfile = NULL;
 
-       int n_subbufs;
-       int subbuf_size;
+struct libustd_instance *instance;
 
-       int file_fd; /* output file */
+struct buffer_info_local {
+       /* output file */
+       int file_fd;
+       /* the offset we must truncate to, to unput the last subbuffer */
+       off_t previous_offset;
 };
 
-int add_buffer(pid_t pid, char *bufname)
+static int write_pidfile(const char *file_name, pid_t pid)
 {
-       struct buffer_info *buf;
-       char *send_msg;
-       char *received_msg;
-       int result;
+       FILE *pidfp;
 
-       buf = (struct buffer_info *) malloc(sizeof(struct buffer_info));
-       if(buf == NULL) {
-               ERR("add_buffer: insufficient memory");
+       pidfp = fopen(file_name, "w");
+       if(!pidfp) {
+               PERROR("fopen (%s)", file_name);
+               WARN("killing child process");
                return -1;
        }
 
-       buf->name = bufname;
-       buf->pid = pid;
+       fprintf(pidfp, "%d\n", pid);
 
-       /* get shmid */
-       asprintf(&send_msg, "get_shmid %s", buf->name);
-       send_message(pid, send_msg, &received_msg);
-       free(send_msg);
-       DBG("got buffer name %s", buf->name);
+       fclose(pidfp);
 
-       result = sscanf(received_msg, "%d", &buf->shmid);
-       if(result != 1) {
-               ERR("unable to parse response to get_shmid");
-               return -1;
+       return 0;
+}
+
+int create_dir_if_needed(char *dir)
+{
+       int result;
+       result = mkdir(dir, 0777);
+       if(result == -1) {
+               if(errno != EEXIST) {
+                       PERROR("mkdir");
+                       return -1;
+               }
        }
-       free(received_msg);
-       DBG("got shmid %d", buf->shmid);
 
-       /* get n_subbufs */
-       asprintf(&send_msg, "get_n_subbufs %s", buf->name);
-       send_message(pid, send_msg, &received_msg);
-       free(send_msg);
+       return 0;
+}
+
+int unwrite_last_subbuffer(struct buffer_info *buf)
+{
+       int result;
+       struct buffer_info_local *buf_local = buf->user_data;
 
-       result = sscanf(received_msg, "%d", &buf->n_subbufs);
-       if(result != 1) {
-               ERR("unable to parse response to get_n_subbufs");
+       result = ftruncate(buf_local->file_fd, buf_local->previous_offset);
+       if(result == -1) {
+               PERROR("ftruncate");
                return -1;
        }
-       free(received_msg);
-       DBG("got n_subbufs %d", buf->n_subbufs);
 
-       /* get subbuf size */
-       asprintf(&send_msg, "get_subbuf_size %s", buf->name);
-       send_message(pid, send_msg, &received_msg);
-       free(send_msg);
+       result = lseek(buf_local->file_fd, buf_local->previous_offset, SEEK_SET);
+       if(result == (int)(off_t)-1) {
+               PERROR("lseek");
+               return -1;
+       }
 
-       result = sscanf(received_msg, "%d", &buf->subbuf_size);
-       if(result != 1) {
-               ERR("unable to parse response to get_subbuf_size");
+       return 0;
+}
+
+int write_current_subbuffer(struct buffer_info *buf)
+{
+       int result;
+       struct buffer_info_local *buf_local = buf->user_data;
+
+       void *subbuf_mem = buf->mem + (buf->consumed_old & (buf->n_subbufs * buf->subbuf_size-1));
+
+       size_t cur_sb_size = subbuffer_data_size(subbuf_mem);
+
+       off_t cur_offset = lseek(buf_local->file_fd, 0, SEEK_CUR);
+       if(cur_offset == (off_t)-1) {
+               PERROR("lseek");
                return -1;
        }
-       free(received_msg);
-       DBG("got subbuf_size %d", buf->subbuf_size);
 
-       /* attach memory */
-       buf->mem = shmat(buf->shmid, NULL, 0);
-       if(buf->mem == (void *) 0) {
-               perror("shmat");
+       buf_local->previous_offset = cur_offset;
+       DBG("previous_offset: %ld", cur_offset);
+
+       result = patient_write(buf_local->file_fd, subbuf_mem, cur_sb_size);
+       if(result == -1) {
+               PERROR("write");
                return -1;
        }
-       DBG("successfully attached memory");
 
        return 0;
 }
 
-int main(int argc, char **argv)
+int on_read_subbuffer(struct libustd_callbacks *data, struct buffer_info *buf)
 {
-       struct ustcomm_ustd ustd;
+       return write_current_subbuffer(buf);
+}
+
+int on_read_partial_subbuffer(struct libustd_callbacks *data, struct buffer_info *buf,
+                               long subbuf_index, unsigned long valid_length)
+{
+       struct buffer_info_local *buf_local = buf->user_data;
+       char *tmp;
        int result;
+       unsigned long pad_size;
 
-       result = ustcomm_init_ustd(&ustd);
+       result = patient_write(buf_local->file_fd, buf->mem + subbuf_index * buf->subbuf_size, valid_length);
        if(result == -1) {
-               ERR("failed to initialize socket");
+               ERR("Error writing to buffer file");
+               return;
+       }
+
+       /* pad with empty bytes */
+       pad_size = PAGE_ALIGN(valid_length)-valid_length;
+       if(pad_size) {
+               tmp = zmalloc(pad_size);
+               result = patient_write(buf_local->file_fd, tmp, pad_size);
+               if(result == -1) {
+                       ERR("Error writing to buffer file");
+                       return;
+               }
+               free(tmp);
+       }
+
+}
+
+int on_open_buffer(struct libustd_callbacks *data, struct buffer_info *buf)
+{
+       char *tmp;
+       int result;
+       int fd;
+       struct buffer_info_local *buf_local =
+               zmalloc(sizeof(struct buffer_info_local));
+
+       if(!buf_local) {
+               ERR("could not allocate buffer_info_local struct");
                return 1;
        }
 
-       for(;;) {
-               char *recvbuf;
+       buf->user_data = buf_local;
+
+       /* open file for output */
+       if(!trace_path) {
+               /* Only create the directory if using the default path, because
+                * of the risk of typo when using trace path override. We don't
+                * want to risk creating plenty of useless directories in that case.
+                */
+               result = create_dir_if_needed(USTD_DEFAULT_TRACE_PATH);
+               if(result == -1) {
+                       ERR("could not create directory %s", USTD_DEFAULT_TRACE_PATH);
+                       return 1;
+               }
+
+               trace_path = USTD_DEFAULT_TRACE_PATH;
+       }
+
+       asprintf(&tmp, "%s/%u_%lld", trace_path, buf->pid, buf->pidunique);
+       result = create_dir_if_needed(tmp);
+       if(result == -1) {
+               ERR("could not create directory %s", tmp);
+               free(tmp);
+               return 1;
+       }
+       free(tmp);
+
+       asprintf(&tmp, "%s/%u_%lld/%s", trace_path, buf->pid, buf->pidunique, buf->name);
+       result = fd = open(tmp, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 00600);
+       if(result == -1) {
+               PERROR("open");
+               ERR("failed opening trace file %s", tmp);
+               return 1;
+       }
+       buf_local->file_fd = fd;
+       free(tmp);
+
+       return 0;
+}
+
+int on_close_buffer(struct libustd_callbacks *data, struct buffer_info *buf)
+{
+       struct buffer_info_local *buf_local = buf->user_data;
+       int result = close(buf_local->file_fd);
+       free(buf_local);
+       if(result == -1) {
+               PERROR("close");
+       }
+       return 0;
+}
+
+int on_put_error(struct libustd_callbacks *data, struct buffer_info *buf)
+{
+       unwrite_last_subbuffer(buf);
+}
+
+struct libustd_callbacks *new_callbacks()
+{
+       struct libustd_callbacks *callbacks =
+               zmalloc(sizeof(struct libustd_callbacks));
+
+       if(!callbacks)
+               return NULL;
+
+       callbacks->on_open_buffer = on_open_buffer;
+       callbacks->on_close_buffer = on_close_buffer;
+       callbacks->on_read_subbuffer = on_read_subbuffer;
+       callbacks->on_read_partial_subbuffer = on_read_partial_subbuffer;
+       callbacks->on_put_error = on_put_error;
+       callbacks->on_new_thread = NULL;
+       callbacks->on_close_thread = NULL;
+       callbacks->on_trace_end = NULL;
+
+       return callbacks;
+
+}
+
+int is_directory(const char *dir)
+{
+       int result;
+       struct stat st;
+
+       result = stat(dir, &st);
+       if(result == -1) {
+               PERROR("stat");
+               return 0;
+       }
+
+       if(!S_ISDIR(st.st_mode)) {
+               return 0;
+       }
+
+       return 1;
+}
+
+void usage(void)
+{
+       fprintf(stderr, "Usage:\nustd OPTIONS\n\nOptions:\n"
+                       "\t-h\t\tDisplay this usage.\n"
+                       "\t-o DIR\t\tSpecify the directory where to output the traces.\n"
+                       "\t-s PATH\t\tSpecify the path to use for the daemon socket.\n"
+                       "\t-d\t\tStart as a daemon.\n"
+                       "\t--pidfile FILE\tWrite the PID in this file (when using -d).\n");
+}
+
+int parse_args(int argc, char **argv)
+{
+       int c;
 
-               ustcomm_ustd_recv_message(&ustd, &recvbuf, NULL);
+       while (1) {
+               int option_index = 0;
+               static struct option long_options[] = {
+                       {"pidfile", 1, 0, 'p'},
+                       {"help", 0, 0, 'h'},
+                       {"version", 0, 0, 'V'},
+                       {0, 0, 0, 0}
+               };
 
-               if(!strncmp(recvbuf, "collect", 7)) {
-                       pid_t pid;
-                       char *bufname;
-                       int result;
+               c = getopt_long(argc, argv, "hs:o:d", long_options, &option_index);
+               if (c == -1)
+                       break;
 
-                       result = sscanf(recvbuf, "%*s %d %50as", &pid, &bufname);
-                       if(result != 2) {
-                               fprintf(stderr, "parsing error: %s\n", recvbuf);
+               switch (c) {
+               case 0:
+                       printf("option %s", long_options[option_index].name);
+                       if (optarg)
+                               printf(" with arg %s", optarg);
+                       printf("\n");
+                       break;
+               case 's':
+                       sock_path = optarg;
+                       break;
+               case 'o':
+                       trace_path = optarg;
+                       if(!is_directory(trace_path)) {
+                               ERR("Not a valid directory. (%s)", trace_path);
+                               return -1;
                        }
+                       break;
+               case 'd':
+                       daemon_mode = 1;
+                       break;
+               case 'p':
+                       pidfile = strdup(optarg);
+                       break;
+               case 'h':
+                       usage();
+                       exit(0);
+               case 'V':
+                       printf("Version 0.0\n");
+                       break;
 
-                       add_buffer(pid, bufname);
-                       
+               default:
+                       /* unknown option or other error; error is
+                       printed by getopt, just return */
+                       return -1;
                }
+       }
+
+       return 0;
+}
+
+void sigterm_handler(int sig)
+{
+       libustd_stop_instance(instance, 0);
+}
+
+int start_ustd(int fd)
+{
+       int result;
+       sigset_t sigset;
+       struct sigaction sa;
 
-               free(recvbuf);
+       struct libustd_callbacks *callbacks = new_callbacks();
+       if(!callbacks) {
+               PERROR("new_callbacks");
+               return 1;
        }
 
+       result = sigemptyset(&sigset);
+       if(result == -1) {
+               PERROR("sigemptyset");
+               return 1;
+       }
+       sa.sa_handler = sigterm_handler;
+       sa.sa_mask = sigset;
+       sa.sa_flags = 0;
+       result = sigaction(SIGTERM, &sa, NULL);
+       if(result == -1) {
+               PERROR("sigaction");
+               return 1;
+       }
+       result = sigaction(SIGINT, &sa, NULL);
+       if(result == -1) {
+               PERROR("sigaction");
+               return 1;
+       }
+
+       instance = libustd_new_instance(callbacks, sock_path);
+       if(!instance) {
+               ERR("failed to create libustd instance");
+               return 1;
+       }
+
+       result = libustd_init_instance(instance);
+       if(result) {
+               ERR("failed to initialize libustd instance");
+               return 1;
+       }
+
+       /* setup handler for SIGPIPE */
+       result = sigemptyset(&sigset);
+       if(result == -1) {
+               PERROR("sigemptyset");
+               return 1;
+       }
+       result = sigaddset(&sigset, SIGPIPE);
+       if(result == -1) {
+               PERROR("sigaddset");
+               return 1;
+       }
+       result = sigprocmask(SIG_BLOCK, &sigset, NULL);
+       if(result == -1) {
+               PERROR("sigprocmask");
+               return 1;
+       }
+
+       /* Write pidfile */
+       if(pidfile) {
+               result = write_pidfile(pidfile, getpid());
+               if(result == -1) {
+                       ERR("failed to write pidfile");
+                       return 1;
+               }
+       }
+
+       /* Notify parent that we are successfully started. */
+       if(fd != -1) {
+               /* write any one character */
+               result = write(fd, "!", 1);
+               if(result == -1) {
+                       PERROR("write");
+                       return -1;
+               }
+               if(result != 1) {
+                       ERR("Problem sending confirmation of daemon start to parent");
+                       return -1;
+               }
+               result = close(fd);
+               if(result == -1) {
+                       PERROR("close");
+               }
+       }
+
+       libustd_start_instance(instance);
+
+       free(callbacks);
+
+       return 0;
+}
+
+int start_ustd_daemon()
+{
+       int result;
+       int fd[2];
+       pid_t child_pid;
+
+       result = pipe(fd);
+
+       result = child_pid = fork();
+       if(result == -1) {
+               PERROR("fork");
+               return -1;
+       }
+       else if(result == 0) {
+               return start_ustd(fd[1]);
+       }
+       else {
+               char buf;
+
+               result = read(fd[0], &buf, 1);
+               if(result == -1) {
+                       PERROR("read");
+                       return -1;
+               }
+               if(result != 1) {
+                       ERR("did not receive valid confirmation that the daemon is started");
+                       return -1;
+               }
+
+               result = close(fd[0]);
+               if(result == -1) {
+                       PERROR("close");
+               }
+
+               DBG("The daemon is now successfully started");
+       }
+
+       /* Wait for confirmation that the server is ready. */
+
+
        return 0;
 }
+
+int main(int argc, char **argv)
+{
+       int result;
+
+       result = parse_args(argc, argv);
+       if(result == -1) {
+               exit(1);
+       }
+
+       if(daemon_mode) {
+               result = start_ustd_daemon();
+       }
+       else {
+               result = start_ustd(-1);
+       }
+
+       return result;
+}
This page took 0.029148 seconds and 4 git commands to generate.