Line data Source code
1 : /*
2 : Authors:
3 : Pavel Březina <pbrezina@redhat.com>
4 :
5 : Copyright (C) 2015 Red Hat
6 :
7 : This program is free software; you can redistribute it and/or modify
8 : it under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3 of the License, or
10 : (at your option) any later version.
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 : You should have received a copy of the GNU General Public License
18 : along with this program. If not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include <talloc.h>
22 : #include <stdlib.h>
23 : #include <string.h>
24 : #include <popt.h>
25 :
26 : #include "config.h"
27 : #include "util/util.h"
28 : #include "confdb/confdb.h"
29 : #include "db/sysdb.h"
30 : #include "tools/common/sss_tools.h"
31 :
32 : struct sss_cmdline {
33 : const char *exec; /* argv[0] */
34 : const char *command; /* command name */
35 : int argc; /* rest of arguments */
36 : const char **argv;
37 : };
38 :
39 0 : static void sss_tool_print_common_opts(void)
40 : {
41 0 : fprintf(stderr, _("Common options:\n"));
42 0 : fprintf(stderr, " --debug=INT %s\n",
43 : _("Enable debug at level"));
44 0 : }
45 :
46 0 : static struct poptOption *sss_tool_common_opts_table(void)
47 : {
48 : static struct poptOption common_opts[] = {
49 : {"debug", '\0', POPT_ARG_INT, NULL,
50 : 0, NULL, NULL },
51 : POPT_TABLEEND
52 : };
53 :
54 0 : common_opts[0].descrip = _("The debug level to run with");
55 :
56 0 : return common_opts;
57 : }
58 :
59 0 : static void sss_tool_common_opts(struct sss_tool_ctx *tool_ctx,
60 : int *argc, const char **argv)
61 : {
62 : poptContext pc;
63 0 : int debug = SSSDBG_DEFAULT;
64 0 : int orig_argc = *argc;
65 : int opt;
66 :
67 0 : struct poptOption options[] = {
68 : {"debug", '\0', POPT_ARG_INT | POPT_ARGFLAG_STRIP, &debug,
69 0 : 0, _("The debug level to run with"), NULL },
70 : POPT_TABLEEND
71 : };
72 :
73 0 : pc = poptGetContext(argv[0], orig_argc, argv, options, 0);
74 0 : while ((opt = poptGetNextOpt(pc)) != -1) {
75 : /* do nothing */
76 : }
77 :
78 : /* Strip common options from arguments. We will discard_const here,
79 : * since it is not worth the trouble to convert it back and forth. */
80 0 : *argc = poptStrippedArgv(pc, orig_argc, discard_const_p(char *, argv));
81 :
82 0 : DEBUG_CLI_INIT(debug);
83 :
84 0 : poptFreeContext(pc);
85 0 : }
86 :
87 0 : static errno_t sss_tool_confdb_init(TALLOC_CTX *mem_ctx,
88 : struct confdb_ctx **_confdb)
89 : {
90 : struct confdb_ctx *confdb;
91 : char *path;
92 : errno_t ret;
93 :
94 0 : path = talloc_asprintf(mem_ctx, "%s/%s", DB_PATH, CONFDB_FILE);
95 0 : if (path == NULL) {
96 0 : return ENOMEM;
97 : }
98 :
99 0 : ret = confdb_init(mem_ctx, &confdb, path);
100 0 : if (ret != EOK) {
101 0 : DEBUG(SSSDBG_CRIT_FAILURE,
102 : "Could not initialize connection to the confdb\n");
103 0 : talloc_free(path);
104 0 : return ret;
105 : }
106 :
107 0 : if (_confdb != NULL) {
108 0 : *_confdb = confdb;
109 : }
110 :
111 0 : return EOK;
112 : }
113 :
114 0 : static errno_t sss_tool_domains_init(TALLOC_CTX *mem_ctx,
115 : struct confdb_ctx *confdb,
116 : struct sss_domain_info **_domains)
117 : {
118 : struct sss_domain_info *domains;
119 : struct sss_domain_info *dom;
120 : errno_t ret;
121 :
122 0 : ret = confdb_get_domains(confdb, &domains);
123 0 : if (ret != EOK) {
124 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup domains [%d]: %s\n",
125 : ret, sss_strerror(ret));
126 0 : return ret;
127 : }
128 :
129 0 : ret = sysdb_init(mem_ctx, domains, false);
130 0 : SYSDB_VERSION_ERROR(ret);
131 0 : if (ret != EOK) {
132 0 : DEBUG(SSSDBG_CRIT_FAILURE,
133 : "Could not initialize connection to the sysdb\n");
134 0 : return ret;
135 : }
136 :
137 0 : for (dom = domains; dom != NULL; dom = get_next_domain(dom, true)) {
138 0 : if (!IS_SUBDOMAIN(dom)) {
139 : /* Update list of subdomains for this domain */
140 0 : ret = sysdb_update_subdomains(dom);
141 0 : if (ret != EOK) {
142 0 : DEBUG(SSSDBG_MINOR_FAILURE,
143 : "Failed to update subdomains for domain %s.\n",
144 : dom->name);
145 : }
146 : }
147 : }
148 :
149 0 : for (dom = domains; dom != NULL; dom = get_next_domain(dom, true)) {
150 0 : ret = sss_names_init(mem_ctx, confdb, dom->name, &dom->names);
151 0 : if (ret != EOK) {
152 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sss_names_init() failed\n");
153 0 : return ret;
154 : }
155 : }
156 :
157 0 : *_domains = domains;
158 :
159 0 : return ret;
160 : }
161 :
162 0 : struct sss_tool_ctx *sss_tool_init(TALLOC_CTX *mem_ctx,
163 : int *argc, const char **argv)
164 : {
165 : struct sss_tool_ctx *tool_ctx;
166 : errno_t ret;
167 :
168 0 : tool_ctx = talloc_zero(mem_ctx, struct sss_tool_ctx);
169 0 : if (tool_ctx == NULL) {
170 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero() failed\n");
171 0 : return NULL;
172 : }
173 :
174 0 : sss_tool_common_opts(tool_ctx, argc, argv);
175 :
176 : /* Connect to confdb. */
177 0 : ret = sss_tool_confdb_init(tool_ctx, &tool_ctx->confdb);
178 0 : if (ret != EOK) {
179 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to open confdb [%d]: %s\n",
180 : ret, sss_strerror(ret));
181 0 : goto done;
182 : }
183 :
184 : /* Setup domains. */
185 0 : ret = sss_tool_domains_init(tool_ctx, tool_ctx->confdb, &tool_ctx->domains);
186 0 : if (ret != EOK) {
187 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup domains [%d]: %s\n",
188 : ret, sss_strerror(ret));
189 0 : goto done;
190 : }
191 :
192 0 : ret = confdb_get_string(tool_ctx->confdb, tool_ctx,
193 : CONFDB_MONITOR_CONF_ENTRY,
194 : CONFDB_MONITOR_DEFAULT_DOMAIN,
195 : NULL, &tool_ctx->default_domain);
196 0 : if (ret != EOK) {
197 0 : DEBUG(SSSDBG_OP_FAILURE, "Cannot get the default domain [%d]: %s\n",
198 : ret, strerror(ret));
199 0 : goto done;
200 : }
201 :
202 0 : ret = EOK;
203 :
204 : done:
205 0 : if (ret != EOK) {
206 0 : talloc_zfree(tool_ctx);
207 : }
208 :
209 0 : return tool_ctx;
210 : }
211 :
212 0 : int sss_tool_usage(const char *tool_name,
213 : struct sss_route_cmd *commands)
214 : {
215 : int i;
216 :
217 0 : fprintf(stderr, _("Usage:\n%s COMMAND COMMAND-ARGS\n\n"), tool_name);
218 0 : fprintf(stderr, _("Available commands:\n"));
219 :
220 0 : for (i = 0; commands[i].command != NULL; i++) {
221 0 : fprintf(stderr, "* %s\n", commands[i].command);
222 : }
223 :
224 0 : fprintf(stderr, _("\n"));
225 0 : sss_tool_print_common_opts();
226 :
227 0 : return EXIT_FAILURE;
228 : }
229 :
230 0 : int sss_tool_route(int argc, const char **argv,
231 : struct sss_tool_ctx *tool_ctx,
232 : struct sss_route_cmd *commands,
233 : void *pvt)
234 : {
235 : struct sss_cmdline cmdline;
236 : const char *cmd;
237 : int i;
238 :
239 0 : if (commands == NULL) {
240 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Bug: commands can't be NULL!\n");
241 0 : return EXIT_FAILURE;
242 : }
243 :
244 0 : if (argc < 2) {
245 0 : return sss_tool_usage(argv[0], commands);
246 : }
247 :
248 0 : cmd = argv[1];
249 0 : for (i = 0; commands[i].command != NULL; i++) {
250 0 : if (strcmp(commands[i].command, cmd) == 0) {
251 0 : cmdline.exec = argv[0];
252 0 : cmdline.command = argv[1];
253 0 : cmdline.argc = argc - 2;
254 0 : cmdline.argv = argv + 2;
255 :
256 0 : return commands[i].fn(&cmdline, tool_ctx, pvt);
257 : }
258 : }
259 :
260 0 : return sss_tool_usage(argv[0], commands);
261 : }
262 :
263 0 : int sss_tool_popt_ex(struct sss_cmdline *cmdline,
264 : struct poptOption *options,
265 : enum sss_tool_opt require_option,
266 : sss_popt_fn popt_fn,
267 : void *popt_fn_pvt,
268 : const char *fopt_name,
269 : const char *fopt_help,
270 : const char **_fopt)
271 : {
272 0 : struct poptOption opts_table[] = {
273 : {NULL, '\0', POPT_ARG_INCLUDE_TABLE, options, \
274 0 : 0, _("Command options:"), NULL },
275 0 : {NULL, '\0', POPT_ARG_INCLUDE_TABLE, sss_tool_common_opts_table(), \
276 0 : 0, _("Common options:"), NULL },
277 : POPT_AUTOHELP
278 : POPT_TABLEEND
279 : };
280 : char *help;
281 : poptContext pc;
282 : int ret;
283 :
284 : /* Create help option string. We always need to append command name since
285 : * we use POPT_CONTEXT_KEEP_FIRST. */
286 0 : if (fopt_name == NULL) {
287 0 : help = talloc_asprintf(NULL, "%s %s %s", cmdline->exec,
288 : cmdline->command, _("[OPTIONS...]"));
289 : } else {
290 0 : help = talloc_asprintf(NULL, "%s %s %s %s", cmdline->exec,
291 : cmdline->command, fopt_name, _("[OPTIONS...]"));
292 : }
293 0 : if (help == NULL) {
294 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n");
295 0 : return EXIT_FAILURE;
296 : }
297 :
298 : /* Create popt context. This function is supposed to be called on
299 : * command argv which does not contain executable (argv[0]), therefore
300 : * we need to use KEEP_FIRST that ensures argv[0] is also processed. */
301 0 : pc = poptGetContext(cmdline->exec, cmdline->argc, cmdline->argv,
302 : opts_table, POPT_CONTEXT_KEEP_FIRST);
303 :
304 0 : poptSetOtherOptionHelp(pc, help);
305 :
306 : /* Parse options. Invoke custom function if provided. If no parsing
307 : * function is provided, print error on unknown option. */
308 0 : while ((ret = poptGetNextOpt(pc)) != -1) {
309 0 : if (popt_fn != NULL) {
310 0 : ret = popt_fn(pc, ret, popt_fn_pvt);
311 0 : if (ret != EOK) {
312 0 : ret = EXIT_FAILURE;
313 0 : goto done;
314 : }
315 : } else {
316 0 : fprintf(stderr, _("Invalid option %s: %s\n\n"),
317 : poptBadOption(pc, 0), poptStrerror(ret));
318 0 : poptPrintHelp(pc, stderr, 0);
319 0 : ret = EXIT_FAILURE;
320 0 : goto done;
321 : }
322 : }
323 :
324 : /* Parse free option which is always required if requested. */
325 0 : if (_fopt != NULL) {
326 0 : *_fopt = poptGetArg(pc);
327 0 : if (*_fopt == NULL) {
328 0 : fprintf(stderr, _("Missing option: %s\n\n"), fopt_help);
329 0 : poptPrintHelp(pc, stderr, 0);
330 0 : ret = EXIT_FAILURE;
331 0 : goto done;
332 : }
333 :
334 : /* No more arguments expected. If something follows it is an error. */
335 0 : if (poptGetArg(pc)) {
336 0 : fprintf(stderr, _("Only one free argument is expected!\n\n"));
337 0 : poptPrintHelp(pc, stderr, 0);
338 0 : ret = EXIT_FAILURE;
339 0 : goto done;
340 : }
341 : }
342 :
343 : /* If at least one option is required and not provided, print error. */
344 0 : if (require_option == SSS_TOOL_OPT_REQUIRED
345 0 : && ((_fopt != NULL && cmdline->argc < 2) || cmdline->argc < 1)) {
346 0 : fprintf(stderr, _("At least one option is required!\n\n"));
347 0 : poptPrintHelp(pc, stderr, 0);
348 0 : ret = EXIT_FAILURE;
349 0 : goto done;
350 : }
351 :
352 0 : ret = EXIT_SUCCESS;
353 :
354 : done:
355 0 : poptFreeContext(pc);
356 0 : talloc_free(help);
357 0 : return ret;
358 : }
359 :
360 0 : int sss_tool_popt(struct sss_cmdline *cmdline,
361 : struct poptOption *options,
362 : enum sss_tool_opt require_option,
363 : sss_popt_fn popt_fn,
364 : void *popt_fn_pvt)
365 : {
366 0 : return sss_tool_popt_ex(cmdline, options, require_option,
367 : popt_fn, popt_fn_pvt, NULL, NULL, NULL);
368 : }
369 :
370 0 : int sss_tool_main(int argc, const char **argv,
371 : struct sss_route_cmd *commands,
372 : void *pvt)
373 : {
374 : struct sss_tool_ctx *tool_ctx;
375 : uid_t uid;
376 : int ret;
377 :
378 0 : uid = getuid();
379 0 : if (uid != 0) {
380 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Running under %d, must be root\n", uid);
381 0 : ERROR("%1$s must be run as root\n", argv[0]);
382 0 : return EXIT_FAILURE;
383 : }
384 :
385 0 : tool_ctx = sss_tool_init(NULL, &argc, argv);
386 0 : if (tool_ctx == NULL) {
387 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tool context\n");
388 0 : return EXIT_FAILURE;
389 : }
390 :
391 0 : ret = sss_tool_route(argc, argv, tool_ctx, commands, pvt);
392 0 : talloc_free(tool_ctx);
393 :
394 0 : return ret;
395 : }
396 :
397 0 : int sss_tool_parse_name(TALLOC_CTX *mem_ctx,
398 : struct sss_tool_ctx *tool_ctx,
399 : const char *input,
400 : const char **_username,
401 : struct sss_domain_info **_domain)
402 : {
403 0 : char *username = NULL;
404 0 : char *domname = NULL;
405 : struct sss_domain_info *domain;
406 : int ret;
407 :
408 0 : ret = sss_parse_name_for_domains(mem_ctx, tool_ctx->domains,
409 0 : tool_ctx->default_domain, input,
410 : &domname, &username);
411 0 : if (ret == EAGAIN) {
412 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to find domain. The domain name may "
413 : "be a subdomain that was not yet found.\n");
414 0 : goto done;
415 0 : } else if (ret != EOK) {
416 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse name [%d]: %s\n",
417 : ret, sss_strerror(ret));
418 0 : goto done;
419 : }
420 :
421 0 : domain = find_domain_by_name(tool_ctx->domains, domname, true);
422 :
423 0 : *_username = username;
424 0 : *_domain = domain;
425 :
426 0 : ret = EOK;
427 :
428 : done:
429 0 : if (ret != EOK) {
430 0 : talloc_zfree(username);
431 0 : talloc_zfree(domname);
432 : }
433 :
434 0 : return ret;
435 : }
|