1 /* Copyright (C) 2009 Pierre-Marc Fournier
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #include <sys/types.h>
36 #include "ust/ustconsumer.h"
37 #include "../libustconsumer/lowlevel.h"
41 char *trace_path
=NULL
;
45 struct ustconsumer_instance
*instance
;
47 struct buffer_info_local
{
50 /* the offset we must truncate to, to unput the last subbuffer */
51 off_t previous_offset
;
54 static int write_pidfile(const char *file_name
, pid_t pid
)
58 pidfp
= fopen(file_name
, "w");
60 PERROR("fopen (%s)", file_name
);
61 WARN("killing child process");
65 fprintf(pidfp
, "%d\n", pid
);
72 int create_dir_if_needed(char *dir
)
75 result
= mkdir(dir
, 0777);
86 int unwrite_last_subbuffer(struct buffer_info
*buf
)
89 struct buffer_info_local
*buf_local
= buf
->user_data
;
91 result
= ftruncate(buf_local
->file_fd
, buf_local
->previous_offset
);
97 result
= lseek(buf_local
->file_fd
, buf_local
->previous_offset
, SEEK_SET
);
98 if(result
== (int)(off_t
)-1) {
106 int write_current_subbuffer(struct buffer_info
*buf
)
109 struct buffer_info_local
*buf_local
= buf
->user_data
;
111 void *subbuf_mem
= buf
->mem
+ (buf
->consumed_old
& (buf
->n_subbufs
* buf
->subbuf_size
-1));
113 size_t cur_sb_size
= subbuffer_data_size(subbuf_mem
);
115 off_t cur_offset
= lseek(buf_local
->file_fd
, 0, SEEK_CUR
);
116 if(cur_offset
== (off_t
)-1) {
121 buf_local
->previous_offset
= cur_offset
;
122 DBG("previous_offset: %ld", cur_offset
);
124 result
= patient_write(buf_local
->file_fd
, subbuf_mem
, cur_sb_size
);
133 int on_read_subbuffer(struct ustconsumer_callbacks
*data
, struct buffer_info
*buf
)
135 return write_current_subbuffer(buf
);
138 int on_read_partial_subbuffer(struct ustconsumer_callbacks
*data
, struct buffer_info
*buf
,
139 long subbuf_index
, unsigned long valid_length
)
141 struct buffer_info_local
*buf_local
= buf
->user_data
;
144 unsigned long pad_size
;
146 result
= patient_write(buf_local
->file_fd
, buf
->mem
+ subbuf_index
* buf
->subbuf_size
, valid_length
);
148 ERR("Error writing to buffer file");
152 /* pad with empty bytes */
153 pad_size
= PAGE_ALIGN(valid_length
)-valid_length
;
155 tmp
= zmalloc(pad_size
);
156 result
= patient_write(buf_local
->file_fd
, tmp
, pad_size
);
158 ERR("Error writing to buffer file");
166 int on_open_buffer(struct ustconsumer_callbacks
*data
, struct buffer_info
*buf
)
171 struct buffer_info_local
*buf_local
=
172 zmalloc(sizeof(struct buffer_info_local
));
175 ERR("could not allocate buffer_info_local struct");
179 buf
->user_data
= buf_local
;
181 /* open file for output */
183 /* Only create the directory if using the default path, because
184 * of the risk of typo when using trace path override. We don't
185 * want to risk creating plenty of useless directories in that case.
187 result
= create_dir_if_needed(USTCONSUMER_DEFAULT_TRACE_PATH
);
189 ERR("could not create directory %s", USTCONSUMER_DEFAULT_TRACE_PATH
);
193 trace_path
= USTCONSUMER_DEFAULT_TRACE_PATH
;
196 if (asprintf(&tmp
, "%s/%u_%" PRId64
"", trace_path
, buf
->pid
, buf
->pidunique
) < 0) {
197 ERR("on_open_buffer : asprintf failed (%s/%u_%" PRId64
")",
198 trace_path
, buf
->pid
, buf
->pidunique
);
201 result
= create_dir_if_needed(tmp
);
203 ERR("could not create directory %s", tmp
);
209 if (asprintf(&tmp
, "%s/%u_%" PRId64
"/%s", trace_path
, buf
->pid
, buf
->pidunique
, buf
->name
) < 0) {
210 ERR("on_open_buffer : asprintf failed (%s/%u_%" PRId64
"/%s)",
211 trace_path
, buf
->pid
, buf
->pidunique
, buf
->name
);
215 result
= fd
= open(tmp
, O_WRONLY
| O_CREAT
| O_TRUNC
| O_EXCL
, 00600);
216 if (result
== -1 && errno
== EINTR
)
221 ERR("failed opening trace file %s", tmp
);
224 buf_local
->file_fd
= fd
;
230 int on_close_buffer(struct ustconsumer_callbacks
*data
, struct buffer_info
*buf
)
232 struct buffer_info_local
*buf_local
= buf
->user_data
;
236 result
= close(buf_local
->file_fd
);
237 if (result
== -1 && errno
== EINTR
)
246 int on_put_error(struct ustconsumer_callbacks
*data
, struct buffer_info
*buf
)
248 return unwrite_last_subbuffer(buf
);
251 struct ustconsumer_callbacks
*new_callbacks()
253 struct ustconsumer_callbacks
*callbacks
=
254 zmalloc(sizeof(struct ustconsumer_callbacks
));
259 callbacks
->on_open_buffer
= on_open_buffer
;
260 callbacks
->on_close_buffer
= on_close_buffer
;
261 callbacks
->on_read_subbuffer
= on_read_subbuffer
;
262 callbacks
->on_read_partial_subbuffer
= on_read_partial_subbuffer
;
263 callbacks
->on_put_error
= on_put_error
;
264 callbacks
->on_new_thread
= NULL
;
265 callbacks
->on_close_thread
= NULL
;
266 callbacks
->on_trace_end
= NULL
;
272 int is_directory(const char *dir
)
277 result
= stat(dir
, &st
);
283 if(!S_ISDIR(st
.st_mode
)) {
292 fprintf(stderr
, "Usage:\nust-consumerd OPTIONS\n\nOptions:\n"
293 "\t-h\t\tDisplay this usage.\n"
294 "\t-o DIR\t\tSpecify the directory where to output the traces.\n"
295 "\t-s PATH\t\tSpecify the path to use for the daemon socket.\n"
296 "\t-d\t\tStart as a daemon.\n"
297 "\t--pidfile FILE\tWrite the PID in this file (when using -d).\n");
300 int parse_args(int argc
, char **argv
)
305 int option_index
= 0;
306 static struct option long_options
[] = {
307 {"pidfile", 1, 0, 'p'},
309 {"version", 0, 0, 'V'},
313 c
= getopt_long(argc
, argv
, "hs:o:d", long_options
, &option_index
);
319 printf("option %s", long_options
[option_index
].name
);
321 printf(" with arg %s", optarg
);
329 if(!is_directory(trace_path
)) {
330 ERR("Not a valid directory. (%s)", trace_path
);
338 pidfile
= strdup(optarg
);
344 printf("Version 0.0\n");
348 /* unknown option or other error; error is
349 printed by getopt, just return */
357 void sigterm_handler(int sig
)
359 ustconsumer_stop_instance(instance
, 0);
362 int start_ustconsumer(int fd
)
368 struct ustconsumer_callbacks
*callbacks
= new_callbacks();
370 PERROR("new_callbacks");
374 result
= sigemptyset(&sigset
);
376 PERROR("sigemptyset");
379 sa
.sa_handler
= sigterm_handler
;
382 result
= sigaction(SIGTERM
, &sa
, NULL
);
387 result
= sigaction(SIGINT
, &sa
, NULL
);
393 instance
= ustconsumer_new_instance(callbacks
, sock_path
);
395 ERR("failed to create ustconsumer instance");
399 result
= ustconsumer_init_instance(instance
);
401 ERR("failed to initialize ustconsumer instance");
405 /* setup handler for SIGPIPE */
406 result
= sigemptyset(&sigset
);
408 PERROR("sigemptyset");
411 result
= sigaddset(&sigset
, SIGPIPE
);
416 result
= sigprocmask(SIG_BLOCK
, &sigset
, NULL
);
418 PERROR("sigprocmask");
424 result
= write_pidfile(pidfile
, getpid());
426 ERR("failed to write pidfile");
431 /* Notify parent that we are successfully started. */
433 /* write any one character */
434 result
= write(fd
, "!", 1);
440 ERR("Problem sending confirmation of daemon start to parent");
449 ustconsumer_start_instance(instance
);
456 int start_ustconsumer_daemon()
464 result
= child_pid
= fork();
469 else if(result
== 0) {
470 return start_ustconsumer(fd
[1]);
475 result
= read(fd
[0], &buf
, 1);
481 ERR("did not receive valid confirmation that the daemon is started");
485 result
= close(fd
[0]);
490 DBG("The daemon is now successfully started");
493 /* Wait for confirmation that the server is ready. */
499 int main(int argc
, char **argv
)
503 result
= parse_args(argc
, argv
);
509 result
= start_ustconsumer_daemon();
512 result
= start_ustconsumer(-1);