Merge branch 'master' into bloom
[adu.git] / adu.c
1 /*
2  * Copyright (C) 2008 Andre Noll <maan@systemlinux.org>
3  *
4  * Licensed under the GPL v2. For licencing details see COPYING.
5  */
6
7 /** \file adu.c \brief The main functions used by all modes of operation. */
8
9 /**
10  * \mainpage adu API reference
11  *
12  * - Modes of operation: \ref select.c, \ref create.c, \ref interactive.c
13  * - User handling: \ref user.c
14  * - Error handling: \ref error.h
15  * - Library-type functions: \ref fd.c, \ref format.c, \ref string.c, \ref portable_io.h, \ref bloom.c
16  */
17
18 #include "adu.h"
19 #include <dirent.h> /* readdir() */
20 #include <pwd.h>
21 #include "format.h"
22 #include "user.h"
23 #include "select.cmdline.h"
24 #include "select.h"
25 #include "cmdline.h"
26 #include "fd.h"
27 #include "string.h"
28 #include "error.h"
29
30 /** Define the array of error descriptions. */
31 DEFINE_ERRLIST;
32
33 /**
34  * The error code of the last called osl library function.
35  *
36  * \sa osl().
37  */
38 int osl_errno;
39
40 /** In case a signal is received, its number is stored here. */
41 static int signum;
42
43 /** Command line and config file options. */
44 struct gengetopt_args_info conf;
45
46 /** Options passed to --select-options. */
47 struct select_args_info select_conf;
48
49
50 /**
51  * The table containing the directory names and statistics.
52  */
53 struct osl_table *dir_table = NULL;
54
55 static struct osl_column_description dir_table_cols[] = {
56         [DT_NAME] = {
57                 .storage_type = OSL_MAPPED_STORAGE,
58                 .storage_flags = 0,
59                 .name = "dir",
60         },
61         [DT_NUM] = {
62                 .storage_type = OSL_MAPPED_STORAGE,
63                 .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE | OSL_UNIQUE,
64                 .name = "num",
65                 .compare_function = uint64_compare,
66                 .data_size = sizeof(uint64_t)
67         },
68         [DT_PARENT_NUM] = {
69                 .storage_type = OSL_MAPPED_STORAGE,
70                 .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE | OSL_UNIQUE,
71                 .name = "parent_num",
72                 .compare_function = size_compare,
73                 .data_size = sizeof(uint64_t)
74         },
75         [DT_BYTES] = {
76                 .storage_type = OSL_MAPPED_STORAGE,
77                 .storage_flags =  OSL_RBTREE | OSL_FIXED_SIZE,
78                 .compare_function = size_compare,
79                 .name = "num_bytes",
80                 .data_size = sizeof(uint64_t)
81         },
82         [DT_FILES] = {
83                 .storage_type = OSL_MAPPED_STORAGE,
84                 .storage_flags =  OSL_RBTREE | OSL_FIXED_SIZE,
85                 .compare_function = size_compare,
86                 .name = "num_files",
87                 .data_size = sizeof(uint64_t)
88         }
89 };
90
91 static struct osl_table_description dir_table_desc = {
92         .name = "dir_table",
93         .num_columns = NUM_DT_COLUMNS,
94         .flags = 0,
95         .column_descriptions = dir_table_cols,
96 };
97
98 /**
99  * The log function.
100  *
101  * \param ll Loglevel.
102  * \param fmt Usual format string.
103  *
104  * All XXX_LOG() macros use this function.
105  */
106 __printf_2_3 void __log(int ll, const char* fmt,...)
107 {
108         va_list argp;
109         FILE *outfd;
110         struct tm *tm;
111         time_t t1;
112         char str[255] = "";
113
114         if (ll < conf.loglevel_arg)
115                 return;
116         outfd = stderr;
117         time(&t1);
118         tm = localtime(&t1);
119         strftime(str, sizeof(str), "%b %d %H:%M:%S", tm);
120         fprintf(outfd, "%s ", str);
121         va_start(argp, fmt);
122         vfprintf(outfd, fmt, argp);
123         va_end(argp);
124 }
125
126 static void close_dir_table(void)
127 {
128         int ret;
129
130         if (!dir_table)
131                 return;
132         NOTICE_LOG("closing dir table\n");
133         ret = osl(osl_close_table(dir_table, OSL_MARK_CLEAN));
134         if (ret < 0)
135                 ERROR_LOG("failed to close dir table: %s\n", adu_strerror(-ret));
136         free((char *)dir_table_desc.dir);
137         dir_table = NULL;
138 }
139
140 static void close_all_tables(void)
141 {
142         close_dir_table();
143         close_user_tables();
144 }
145
146 static void signal_handler(int s)
147 {
148         signum = s;
149 }
150
151 /**
152  * Check whether to terminate adu.
153  *
154  * Check whether a signal was caught that should terminate the
155  * adu process. If yes, close all osl tables and exit gracefully.
156  */
157 void check_signals(void)
158 {
159         if (likely(!signum))
160                 return;
161         EMERG_LOG("caught signal %d\n", signum);
162         close_all_tables();
163         exit(EXIT_FAILURE);
164 }
165
166 static int init_signals(void)
167 {
168         if (signal(SIGINT, &signal_handler) == SIG_ERR)
169                 return -E_SIGNAL_SIG_ERR;
170         if (signal(SIGTERM, &signal_handler) == SIG_ERR)
171                 return -E_SIGNAL_SIG_ERR;
172         if (signal(SIGPIPE, &signal_handler) == SIG_ERR)
173                 return -E_SIGNAL_SIG_ERR;
174         return 1;
175 }
176
177 /**
178  * Open the directory table.
179  *
180  * \param create If non-zero, create the table first.
181  *
182  * \return Standard.
183  */
184 int open_dir_table(int create)
185 {
186
187         if (dir_table)
188                 return 1;
189         dir_table_desc.dir = adu_strdup(conf.database_dir_arg);
190
191         if (create) {
192                 NOTICE_LOG("creating dir table\n");
193                 int ret = osl(osl_create_table(&dir_table_desc));
194                 if (ret < 0) {
195                         free((char *)dir_table_desc.dir);
196                         return ret;
197                 }
198         }
199         INFO_LOG("opening dir table\n");
200         return osl(osl_open_table(&dir_table_desc, &dir_table));
201 }
202
203 static int check_args(void)
204 {
205         if (conf.create_given && !conf.base_dir_given)
206                 return -E_SYNTAX;
207
208         /* remove trailing slashes from base-dir arg */
209         if (conf.base_dir_given) {
210                 size_t len = strlen(conf.base_dir_arg);
211                 for (;;) {
212                         if (!len) /* empty string */
213                                 return -ERRNO_TO_ERROR(EINVAL);
214                         if (!--len) /* length 1 is always OK */
215                                 break;
216                         if (conf.base_dir_arg[len] != '/')
217                                 break; /* no trailing slash, also OK */
218                         conf.base_dir_arg[len] = '\0';
219                 }
220         }
221         return 1;
222 }
223
224 static void print_complete_help_and_die(void)
225 {
226         const char **line;
227
228         printf("%s-%s\n", CMDLINE_PARSER_PACKAGE, CMDLINE_PARSER_VERSION);
229         printf("%s\n\n", gengetopt_args_info_purpose);
230         printf("%s\n\n", gengetopt_args_info_usage);
231
232         if (conf.help_given)
233                 line = gengetopt_args_info_help;
234         else
235                 line = gengetopt_args_info_detailed_help;
236         for (; *line; line++)
237                 printf("%s\n", *line);
238
239         if (conf.help_given)
240                 line = select_args_info_help;
241         else
242                 line  = select_args_info_detailed_help;
243         printf("Select options:\n");
244         for (; *line; line++)
245                 printf("%s\n", *line);
246
247         printf("Interactive commands:\n");
248         print_interactive_help();
249         cmdline_parser_free(&conf);
250         select_cmdline_parser_free(&select_conf);
251         exit(EXIT_FAILURE);
252 }
253
254 /**
255  * The main function of adu.
256  *
257  * \param argc Usual argument count.
258  * \param argv Usual argument vector.
259  *
260  * Check command line options, init the signal handlers and
261  * call the main function of the selected mode.
262  *
263  * \return \p EXIT_SUCCESS on success, \p EXIT_FAILURE otherwise.
264  */
265 int main(int argc, char **argv)
266 {
267         int ret;
268         struct cmdline_parser_params params = {
269                 .override = 1,
270                 .initialize = 1,
271                 .check_required = 0,
272                 .check_ambiguity = 0,
273                 .print_errors = 0
274         };
275         select_cmdline_parser_init(&select_conf);
276         cmdline_parser_init(&conf);
277         /* ignore errors and print complete help if --help was given */
278         cmdline_parser_ext(argc, argv, &conf, &params);
279         if (conf.help_given || conf.detailed_help_given)
280                 print_complete_help_and_die();
281         cmdline_parser_free(&conf);
282         params.check_required = 1;
283         params.check_ambiguity = 1;
284         params.print_errors = 1;
285         ret = cmdline_parser_ext(argc, argv, &conf, &params);
286         if (ret)
287                 exit(EXIT_FAILURE);
288
289         ret = check_args();
290         if (ret < 0)
291                 goto out;
292         ret = init_signals();
293         if (ret < 0)
294                 goto out;
295         ret = -E_SYNTAX;
296         if (conf.select_given)
297                 ret = com_select();
298         else if (conf.create_given)
299                 ret = com_create();
300         else
301                 ret = com_interactive();
302         if (ret < 0)
303                 goto out;
304 out:
305         close_all_tables();
306         if (ret < 0) {
307                 ERROR_LOG("%s\n", adu_strerror(-ret));
308                 return -EXIT_FAILURE;
309         }
310         cmdline_parser_free(&conf);
311         select_cmdline_parser_free(&select_conf);
312         return EXIT_SUCCESS;
313 }