- Possibility to operate with single size entries as well as control of the reclamat...
[mirror/scst/.git] / usr / fileio / fileio.c
1 /*
2  *  fileio.c
3  *
4  *  Copyright (C) 2007 - 2009 Vladislav Bolkhovitin <vst@vlnb.net>
5  *  Copyright (C) 2007 - 2009 ID7 Ltd.
6  *
7  *  This program is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU General Public License
9  *  as published by the Free Software Foundation, version 2
10  *  of the License.
11  *
12  *  This program 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
18 #include <ctype.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <stdint.h>
26 #include <getopt.h>
27 #include <malloc.h>
28 #include <inttypes.h>
29 #include <signal.h>
30 #include <sys/types.h>
31 #include <sys/user.h>
32 #include <sys/poll.h>
33 #include <sys/ioctl.h>
34
35 #include <pthread.h>
36
37 char *app_name;
38
39 #include "common.h"
40
41 #if defined(DEBUG) || defined(TRACING)
42
43 #ifdef DEBUG
44 /*#define DEFAULT_LOG_FLAGS (TRACE_ALL & ~TRACE_MEMORY & ~TRACE_BUFF \
45          & ~TRACE_FUNCTION)
46 #define DEFAULT_LOG_FLAGS (TRACE_ALL & ~TRACE_MEMORY & ~TRACE_BUFF & \
47         ~TRACE_SCSI & ~TRACE_SCSI_SERIALIZING & ~TRACE_DEBUG)
48 */
49 #define DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MINOR | TRACE_PID | \
50         TRACE_FUNCTION | TRACE_SPECIAL | TRACE_MGMT | TRACE_MGMT_MINOR | \
51         TRACE_MGMT_DEBUG | TRACE_TIME)
52
53 #define TRACE_SN(args...)       TRACE(TRACE_SCSI_SERIALIZING, args)
54
55 #else /* DEBUG */
56
57 # ifdef TRACING
58 #define DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MINOR | TRACE_MGMT | \
59         TRACE_TIME | TRACE_SPECIAL)
60 # else
61 #define DEFAULT_LOG_FLAGS 0
62 # endif
63 #endif /* DEBUG */
64
65 unsigned long trace_flag = DEFAULT_LOG_FLAGS;
66 #endif /* defined(DEBUG) || defined(TRACING) */
67
68 #define DEF_BLOCK_SHIFT         9
69 #define VERSION_STR             "1.0.2"
70 #define THREADS                 7
71
72 #define MAX_VDEVS               10
73
74 static void *align_alloc(size_t size);
75
76 static struct vdisk_dev devs[MAX_VDEVS];
77 static int num_devs;
78
79 int vdisk_ID;
80 static int flush_interval;
81
82 static int parse_type = SCST_USER_PARSE_STANDARD;
83 static int on_free_cmd_type = SCST_USER_ON_FREE_CMD_IGNORE;
84 static int on_free_cmd_type_set;
85 static int memory_reuse_type = SCST_USER_MEM_REUSE_ALL;
86 static int threads = THREADS;
87 static int unreg_before_close;
88 static int block_size = (1 << DEF_BLOCK_SHIFT);
89 static int block_shift = DEF_BLOCK_SHIFT;
90 static int wt_flag, rd_only_flag, o_direct_flag, nullio, nv_cache;
91 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
92 static int debug_tm_ignore;
93 #endif
94 static int non_blocking, sgv_shared, sgv_single_alloc_pages, sgv_purge_interval;
95 static void *(*alloc_fn)(size_t size) = align_alloc;
96
97 static struct option const long_options[] =
98 {
99         {"block", required_argument, 0, 'b'},
100         {"threads", required_argument, 0, 'e'},
101         {"write_through", no_argument, 0, 't'},
102         {"read_only", no_argument, 0, 'r'},
103         {"direct", no_argument, 0, 'o'},
104         {"nullio", no_argument, 0, 'n'},
105         {"nv_cache", no_argument, 0, 'c'},
106         {"parse", required_argument, 0, 'p'},
107         {"on_free", required_argument, 0, 'f'},
108         {"mem_reuse", required_argument, 0, 'm'},
109         {"non_blocking", no_argument, 0, 'l'},
110         {"vdisk_id", required_argument, 0, 'I'},
111         {"flush", required_argument, 0, 'F'},
112         {"unreg_before_close", no_argument, 0, 'u'},
113         {"sgv_shared", no_argument, 0, 's'},
114         {"sgv_single_cache", required_argument, 0, 'S'},
115         {"sgv_purge_interval", required_argument, 0, 'P'},
116 #if defined(DEBUG) || defined(TRACING)
117         {"debug", required_argument, 0, 'd'},
118 #endif
119 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
120         {"debug_tm_ignore", no_argument, 0, 'g'},
121 #endif
122         {"version", no_argument, 0, 'v'},
123         {"help", no_argument, 0, 'h'},
124         {0, 0, 0, 0},
125 };
126
127 static void usage(void)
128 {
129         printf("Usage: %s [OPTIONS] name path [name path] ...\n", app_name);
130         printf("\nFILEIO disk target emulator for SCST\n");
131         printf("  -b, --block=size      Block size, must be power of 2 and >=512\n");
132         printf("  -e, --threads=count   Number of threads, %d by default\n", THREADS);
133         printf("  -t, --write_through   Write through mode\n");
134         printf("  -r, --read_only       Read only\n");
135         printf("  -o, --direct          O_DIRECT mode\n");
136         printf("  -n, --nullio          NULLIO mode\n");
137         printf("  -c, --nv_cache        NV_CACHE mode\n");
138         printf("  -p, --parse=type      Parse type, one of \"std\" "
139                 "(default), \"call\" or \"excpt\"\n");
140         printf("  -f, --on_free=type    On free call type, one of \"ignore\" "
141                 "(default) or \"call\"\n");
142         printf("  -m, --mem_reuse=type  Memory reuse type, one of \"all\" "
143                 "(default), \"read\", \"write\" or \"none\"\n");
144         printf("  -l, --non_blocking    Use non-blocking operations\n");
145         printf("  -I, --vdisk_id=ID     Vdisk ID (used in multi-targets setups)\n");
146         printf("  -F, --flush=n         Flush SGV cache each n seconds\n");
147         printf("  -s, --sgv_shared      Use shared SGV cache\n");
148         printf("  -S, --sgv_single_cache=n Use single entry SGV cache with n pages/entry\n");
149         printf("  -P, --sgv_purge_interval=n Use SGV cache purge interval n seconds\n");
150         printf("  -u, --unreg_before_close Unregister before close\n");
151 #if defined(DEBUG) || defined(TRACING)
152         printf("  -d, --debug=level     Debug tracing level\n");
153 #endif
154 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
155         printf("  -g, --debug_tm_ignore Turn on DEBUG_TM_IGNORE\n");
156 #endif
157         return;
158 }
159
160 static int scst_calc_block_shift(int sector_size)
161 {
162         int block_shift = 0;
163         int t = sector_size;
164
165         if (sector_size == 0)
166                 goto out;
167
168         t = sector_size;
169         while(1) {
170                 if ((t & 1) != 0)
171                         break;
172                 t >>= 1;
173                 block_shift++;
174         }
175         if (block_shift < 9) {
176                 PRINT_ERROR("Wrong sector size %d", sector_size);
177                 block_shift = -1;
178         }
179
180 out:
181         TRACE_EXIT_RES(block_shift);
182         return block_shift;
183 }
184
185 static void *align_alloc(size_t size)
186 {
187         return memalign(PAGE_SIZE, size);
188 }
189
190 void sigalrm_handler(int signo)
191 {
192         int res, i;
193
194         TRACE_ENTRY();
195
196         TRACE_DBG("%s", "Flushing cache...");
197
198         for (i = 0; i < num_devs; i++) {
199                 res = ioctl(devs[i].scst_usr_fd, SCST_USER_FLUSH_CACHE, NULL);
200                 if (res != 0) {
201                         res = errno;
202                         PRINT_ERROR("Unable to flush cache: %s", strerror(res));
203                         goto out;
204                 }
205         }
206
207         TRACE_DBG("%s", "Flushing cache done.");
208
209         res = alarm(flush_interval);
210         if (res != 0) {
211                 res = errno;
212                 PRINT_ERROR("alarm() failed: %s", strerror(res));
213                 goto out;
214         }
215
216 out:
217         TRACE_EXIT();
218         return;
219 }
220
221 void sigusr1_handler(int signo)
222 {
223         int res, i;
224
225         TRACE_ENTRY();
226
227         TRACE_MGMT_DBG("%s", "Capacity data changed...");
228
229         for (i = 0; i < num_devs; i++) {
230                 res = ioctl(devs[i].scst_usr_fd, SCST_USER_DEVICE_CAPACITY_CHANGED, NULL);
231                 if (res != 0) {
232                         res = errno;
233                         PRINT_ERROR("Capacity data changed failed: %s", strerror(res));
234                         goto out;
235                 }
236         }
237
238         TRACE_DBG("%s", "Capacity data changed done.");
239
240 out:
241         TRACE_EXIT();
242         return;
243 }
244
245 int start(int argc, char **argv)
246 {
247         int res = 0;
248         int fd;
249         int i, rc;
250         void *rc1;
251         static struct scst_user_dev_desc desc;
252         pthread_t thread[MAX_VDEVS][threads];
253
254         memset(thread, 0, sizeof(thread));
255
256         i = 0;
257         optind -= 2;
258         while (1) {
259                 int j;
260
261                 optind += 2;
262                 if (optind > (argc-2))
263                         break;
264
265                 devs[i].block_size = block_size;
266                 devs[i].block_shift = block_shift;
267                 devs[i].alloc_fn = alloc_fn;
268
269                 devs[i].rd_only_flag = rd_only_flag;
270                 devs[i].wt_flag = wt_flag;
271                 devs[i].nv_cache = nv_cache;
272                 devs[i].o_direct_flag = o_direct_flag;
273                 devs[i].nullio = nullio;
274                 devs[i].non_blocking = non_blocking;
275 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
276                 devs[i].debug_tm_ignore = debug_tm_ignore;
277 #endif
278                 devs[i].type = TYPE_DISK;
279                 devs[i].name = argv[optind];
280                 devs[i].file_name = argv[optind+1];
281
282                 TRACE_DBG("Opening file %s", devs[i].file_name);
283                 fd = open(devs[i].file_name, O_RDONLY|O_LARGEFILE);
284                 if (fd < 0) {
285                         res = -errno;
286                         PRINT_ERROR("Unable to open file %s (%s)", devs[i].file_name,
287                                 strerror(-res));
288                         continue;
289                 }
290
291                 devs[i].file_size = lseek64(fd, 0, SEEK_END);
292                 devs[i].nblocks = devs[i].file_size >> devs[i].block_shift;
293
294                 close(fd);
295
296                 PRINT_INFO("%s", " ");
297                 PRINT_INFO("Virtual device \"%s\", path \"%s\", size %"PRId64"Mb, "
298                         "block size %d, nblocks %"PRId64", options:", devs[i].name,
299                         devs[i].file_name, (uint64_t)devs[i].file_size/1024/1024,
300                         devs[i].block_size, (uint64_t)devs[i].nblocks);
301
302                 snprintf(devs[i].usn, sizeof(devs[i].usn), "%"PRIx64,
303                         gen_dev_id_num(&devs[i]));
304                 TRACE_DBG("usn %s", devs[i].usn);
305
306                 devs[i].scst_usr_fd = open(DEV_USER_PATH DEV_USER_NAME, O_RDWR |
307                                         (devs[i].non_blocking ? O_NONBLOCK : 0));
308                 if (devs[i].scst_usr_fd < 0) {
309                         res = -errno;
310                         PRINT_ERROR("Unable to open SCST device %s (%s)",
311                                 DEV_USER_PATH DEV_USER_NAME, strerror(-res));
312                         goto out_unreg;
313                 }
314
315                 memset(&desc, 0, sizeof(desc));
316                 desc.version_str = (unsigned long)DEV_USER_VERSION;
317                 strncpy(desc.name, devs[i].name, sizeof(desc.name)-1);
318                 desc.name[sizeof(desc.name)-1] = '\0';
319                 if (sgv_shared) {
320                         desc.sgv_shared = 1;
321                         strncpy(desc.sgv_name, devs[0].name, sizeof(desc.sgv_name)-1);
322                         desc.sgv_name[sizeof(desc.sgv_name)-1] = '\0';
323                 }
324                 desc.sgv_single_alloc_pages = sgv_single_alloc_pages;
325                 desc.sgv_purge_interval = sgv_purge_interval;
326                 desc.type = devs[i].type;
327                 desc.block_size = devs[i].block_size;
328
329                 desc.opt.parse_type = parse_type;
330                 desc.opt.on_free_cmd_type = on_free_cmd_type;
331                 desc.opt.memory_reuse_type = memory_reuse_type;
332
333                 desc.opt.tst = SCST_CONTR_MODE_SEP_TASK_SETS;
334                 desc.opt.queue_alg = SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER;
335                 desc.opt.d_sense = SCST_CONTR_MODE_FIXED_SENSE;
336
337                 res = ioctl(devs[i].scst_usr_fd, SCST_USER_REGISTER_DEVICE, &desc);
338                 if (res != 0) {
339                         res = errno;
340                         PRINT_ERROR("Unable to register device: %s", strerror(res));
341                         goto out_unreg;
342                 }
343
344 #if 1
345                 {
346                         /* Not needed, added here only as a test */
347                         struct scst_user_opt opt;
348
349                         res = ioctl(devs[i].scst_usr_fd, SCST_USER_GET_OPTIONS, &opt);
350                         if (res != 0) {
351                                 res = errno;
352                                 PRINT_ERROR("Unable to get options: %s", strerror(res));
353                                 goto out_unreg;
354                         }
355
356                         opt.parse_type = parse_type;
357                         opt.on_free_cmd_type = on_free_cmd_type;
358                         opt.memory_reuse_type = memory_reuse_type;
359
360                         res = ioctl(devs[i].scst_usr_fd, SCST_USER_SET_OPTIONS, &opt);
361                         if (res != 0) {
362                                 res = errno;
363                                 PRINT_ERROR("Unable to set options: %s", strerror(res));
364                                 goto out_unreg;
365                         }
366                 }
367 #endif
368
369                 res = pthread_mutex_init(&devs[i].dev_mutex, NULL);
370                 if (res != 0) {
371                         res = errno;
372                         PRINT_ERROR("pthread_mutex_init() failed: %s", strerror(res));
373                         goto out_unreg;
374                 }
375
376                 for (j = 0; j < threads; j++) {
377                         rc = pthread_create(&thread[i][j], NULL, main_loop, &devs[i]);
378                         if (rc != 0) {
379                                 res = errno;
380                                 PRINT_ERROR("pthread_create() failed: %s",
381                                         strerror(res));
382                                 break;
383                         }
384                 }
385
386                 i++;
387                 num_devs++;
388                 if (num_devs >= MAX_VDEVS) {
389                         PRINT_INFO("Max devices limit %d reached", i);
390                         break;
391                 }
392         }
393
394         for (i = 0; i < num_devs; i++) {
395                 int j = 0;
396                 while (thread[i][j] != 0) {
397                         rc = pthread_join(thread[i][j], &rc1);
398                         if (rc != 0) {
399                                 res = errno;
400                                 PRINT_ERROR("pthread_join() failed: %s",
401                                         strerror(res));
402                         } else if (rc1 != NULL) {
403                                 res = (long)rc1;
404                                 PRINT_INFO("Thread %d exited (dev %s), res %lx", j,
405                                         devs[i].name, (long)rc1);
406                         } else
407                                 PRINT_INFO("Thread %d exited (dev %s)", j,
408                                         devs[i].name);
409                         j++;
410                 }
411                 pthread_mutex_destroy(&devs[i].dev_mutex);
412         }
413
414 out_unreg:
415         alarm(0);
416         for (i = 0; i < num_devs; i++) {
417                 if (unreg_before_close) {
418                         res = ioctl(devs[i].scst_usr_fd, SCST_USER_UNREGISTER_DEVICE, NULL);
419                         if (res != 0) {
420                                 res = errno;
421                                 PRINT_ERROR("Unable to unregister device: %s",
422                                         strerror(res));
423                                 /* go through */
424                         }
425                 }
426                 close(devs[i].scst_usr_fd);
427         }
428
429         return res;
430 }
431
432 int main(int argc, char **argv)
433 {
434         int res = 0;
435         int ch, longindex;
436         struct sigaction act;
437
438         setlinebuf(stdout);
439
440         res = debug_init();
441         if (res != 0)
442                 goto out;
443
444         app_name = argv[0];
445
446         memset(devs, 0, sizeof(devs));
447
448         while ((ch = getopt_long(argc, argv, "+b:e:trongluF:I:cp:f:m:d:vsS:P:h", long_options,
449                                 &longindex)) >= 0) {
450                 switch (ch) {
451                 case 'b':
452                         block_size = atoi(optarg);
453                         PRINT_INFO("block_size %x (%s)", block_size, optarg);
454                         block_shift = scst_calc_block_shift(block_size);
455                         if (block_shift < 9) {
456                                 res = -EINVAL;
457                                 goto out_usage;
458                         }
459                         break;
460                 case 'e':
461                         threads = strtol(optarg, (char **)NULL, 0);
462                         break;
463                 case 't':
464                         wt_flag = 1;
465                         break;
466 #if defined(DEBUG) || defined(TRACING)
467                 case 'd':
468                         trace_flag = strtol(optarg, (char **)NULL, 0);
469                         break;
470 #endif
471                 case 'r':
472                         rd_only_flag = 1;
473                         break;
474                 case 'o':
475                         o_direct_flag = 1;
476                         break;
477                 case 'n':
478                         nullio = 1;
479                         break;
480                 case 'c':
481                         nv_cache = 1;
482                         break;
483                 case 'p':
484                         if (strncmp(optarg, "std", 3) == 0)
485                                 parse_type = SCST_USER_PARSE_STANDARD;
486                         else if (strncmp(optarg, "call", 3) == 0)
487                                 parse_type = SCST_USER_PARSE_CALL;
488                         else if (strncmp(optarg, "excpt", 5) == 0)
489                                 parse_type = SCST_USER_PARSE_EXCEPTION;
490                         else
491                                 goto out_usage;
492                         break;
493                 case 'f':
494                         on_free_cmd_type_set = 1;
495                         if (strncmp(optarg, "ignore", 6) == 0)
496                                 on_free_cmd_type = SCST_USER_ON_FREE_CMD_IGNORE;
497                         else if (strncmp(optarg, "call", 3) == 0)
498                                 on_free_cmd_type = SCST_USER_ON_FREE_CMD_CALL;
499                         else
500                                 goto out_usage;
501                         break;
502                 case 's':
503                         sgv_shared = 1;
504                         break;
505                 case 'S':
506                         sgv_single_alloc_pages = atoi(optarg);
507                         break;
508                 case 'P':
509                         sgv_purge_interval = atoi(optarg);
510                         break;
511                 case 'm':
512                         if (strncmp(optarg, "all", 3) == 0)
513                                 memory_reuse_type = SCST_USER_MEM_REUSE_ALL;
514                         else if (strncmp(optarg, "read", 4) == 0)
515                                 memory_reuse_type = SCST_USER_MEM_REUSE_READ;
516                         else if (strncmp(optarg, "write", 5) == 0)
517                                 memory_reuse_type = SCST_USER_MEM_REUSE_WRITE;
518                         else if (strncmp(optarg, "none", 4) == 0)
519                                 memory_reuse_type = SCST_USER_MEM_NO_REUSE;
520                         else
521                                 goto out_usage;
522                         break;
523                 case 'l':
524                         non_blocking = 1;
525                         break;
526                 case 'I':
527                         vdisk_ID = strtol(optarg, (char **)NULL, 0);
528                         break;
529                 case 'F':
530                         flush_interval = strtol(optarg, (char **)NULL, 0);
531                         if (flush_interval < 0) {
532                                 PRINT_ERROR("Wrong flush interval %d",
533                                         flush_interval);
534                                 flush_interval = 0;
535                         }
536                         break;
537                 case 'u':
538                         unreg_before_close = 1;
539                         break;
540 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
541                 case 'g':
542                         debug_tm_ignore = 1;
543                         break;
544 #endif
545                 case 'v':
546                         printf("%s version %s\n", app_name, VERSION_STR);
547                         goto out_done;
548                 default:
549                         goto out_usage;
550                 }
551         }
552
553         if (optind > (argc-2))
554                 goto out_usage;
555
556         if (!on_free_cmd_type_set &&
557             (memory_reuse_type != SCST_USER_MEM_REUSE_ALL))
558                 on_free_cmd_type = SCST_USER_ON_FREE_CMD_CALL;
559
560         PRINT_INFO("%s", "Options:");
561
562         if (rd_only_flag)
563                 PRINT_INFO("    %s", "READ ONLY");
564         if (wt_flag)
565                 PRINT_INFO("    %s", "WRITE THROUGH");
566         if (nv_cache)
567                 PRINT_INFO("    %s", "NV_CACHE");
568         if (o_direct_flag)
569                 PRINT_INFO("    %s", "O_DIRECT");
570         if (nullio)
571                 PRINT_INFO("    %s", "NULLIO");
572         if (non_blocking)
573                 PRINT_INFO("    %s", "NON-BLOCKING");
574
575         switch(parse_type) {
576         case SCST_USER_PARSE_STANDARD:
577                 PRINT_INFO("    %s", "Standard parse");
578                 break;
579         case SCST_USER_PARSE_CALL:
580                 PRINT_INFO("    %s", "Call parse");
581                 break;
582         case SCST_USER_PARSE_EXCEPTION:
583                 PRINT_INFO("    %s", "Exception parse");
584                 break;
585         default:
586                 sBUG();
587         }
588
589         switch(on_free_cmd_type) {
590         case SCST_USER_ON_FREE_CMD_IGNORE:
591                 PRINT_INFO("    %s", "Ignore on_free_cmd");
592                 break;
593         case SCST_USER_ON_FREE_CMD_CALL:
594                 PRINT_INFO("    %s", "Call on_free_cmd");
595                 break;
596         default:
597                 sBUG();
598         }
599
600         switch(memory_reuse_type) {
601         case SCST_USER_MEM_REUSE_ALL:
602                 PRINT_INFO("    %s", "Full memory reuse enabled");
603                 break;
604         case SCST_USER_MEM_REUSE_READ:
605                 PRINT_INFO("    %s", "READ memory reuse enabled");
606                 break;
607         case SCST_USER_MEM_REUSE_WRITE:
608                 PRINT_INFO("    %s", "WRITE memory reuse enabled");
609                 break;
610         case SCST_USER_MEM_NO_REUSE:
611                 PRINT_INFO("    %s", "Memory reuse disabled");
612                 break;
613         default:
614                 sBUG();
615         }
616
617         if (sgv_shared)
618                 PRINT_INFO("    %s", "SGV shared");
619
620         if (sgv_single_alloc_pages != 0)
621                 PRINT_INFO("    Use single entry SGV cache with %d pages/entry",
622                         sgv_single_alloc_pages);
623
624         if (sgv_purge_interval != 0) {
625                 if (sgv_purge_interval > 0)
626                         PRINT_INFO("    Use SGV cache purge interval %d seconds",
627                                 sgv_purge_interval);
628                 else
629                         PRINT_INFO("    %s", "SGV cache purging disabled");
630         }
631
632         if (!o_direct_flag && (memory_reuse_type == SCST_USER_MEM_NO_REUSE)) {
633                 PRINT_INFO("    %s", "Using unaligned buffers");
634                 alloc_fn = malloc;
635         }
636
637 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
638         if (debug_tm_ignore)
639                 PRINT_INFO("    %s", "DEBUG_TM_IGNORE");
640 #endif
641
642 #ifdef DEBUG
643         PRINT_INFO("trace_flag %lx", trace_flag);
644 #endif
645
646         memset(&act, 0, sizeof(act));
647         act.sa_handler = sigusr1_handler;
648         act.sa_flags = SA_RESTART;
649         sigemptyset(&act.sa_mask);
650         res = sigaction(SIGUSR1, &act, NULL);
651         if (res != 0) {
652                 res = errno;
653                 PRINT_ERROR("sigaction() failed: %s",
654                         strerror(res));
655                 /* don't do anything */
656         }
657
658         if (flush_interval != 0) {
659                 memset(&act, 0, sizeof(act));
660                 act.sa_handler = sigalrm_handler;
661                 act.sa_flags = SA_RESTART;
662                 sigemptyset(&act.sa_mask);
663                 res = sigaction(SIGALRM, &act, NULL);
664                 if (res != 0) {
665                         res = errno;
666                         PRINT_ERROR("sigaction() failed: %s",
667                                 strerror(res));
668                         goto out_done;
669                 }       
670
671                 res = alarm(flush_interval);
672                 if (res != 0) {
673                         res = errno;
674                         PRINT_ERROR("alarm() failed: %s",
675                                 strerror(res));
676                         goto out_done;
677                 }
678         }
679
680         res = start(argc, argv);
681
682 out_done:
683         debug_done();
684
685 out:
686         return res;
687
688 out_usage:
689         usage();
690         goto out_done;
691 }