39ca6fe11c14eae35c4f1b928a564708876f19bd
[multipath-tools/.git] / multipathd / cli_handlers.c
1 /*
2  * Copyright (c) 2005 Christophe Varoqui
3  */
4 #include <checkers.h>
5 #include <memory.h>
6 #include <vector.h>
7 #include <structs.h>
8 #include <structs_vec.h>
9 #include <libdevmapper.h>
10 #include <devmapper.h>
11 #include <config.h>
12 #include <configure.h>
13 #include <blacklist.h>
14 #include <debug.h>
15 #include <print.h>
16 #include <sysfs.h>
17 #include <errno.h>
18
19 #include "main.h"
20 #include "cli.h"
21
22 #define REALLOC_REPLY(r, a, m)                                  \
23         do {                                                    \
24                 if ((a)) {                                      \
25                         (r) = REALLOC((r), (m) * 2);            \
26                         if ((r)) {                              \
27                                 memset((r) + (m), 0, (m));      \
28                                 (m) *= 2;                       \
29                         }                                       \
30                 }                                               \
31         } while (0)
32
33 int
34 show_paths (char ** r, int * len, struct vectors * vecs, char * style)
35 {
36         int i;
37         struct path * pp;
38         char * c;
39         char * reply;
40         unsigned int maxlen = INITIAL_REPLY_LEN;
41         int again = 1;
42
43         get_path_layout(vecs->pathvec, 1);
44         reply = MALLOC(maxlen);
45
46         while (again) {
47                 if (!reply)
48                         return 1;
49
50                 c = reply;
51
52                 if (VECTOR_SIZE(vecs->pathvec) > 0)
53                         c += snprint_path_header(c, reply + maxlen - c,
54                                                  style);
55
56                 vector_foreach_slot(vecs->pathvec, pp, i)
57                         c += snprint_path(c, reply + maxlen - c,
58                                           style, pp);
59
60                 again = ((c - reply) == (maxlen - 1));
61
62                 REALLOC_REPLY(reply, again, maxlen);
63         }
64         *r = reply;
65         *len = (int)(c - reply + 1);
66         return 0;
67 }
68
69 int
70 show_map_topology (char ** r, int * len, struct multipath * mpp)
71 {
72         char * c;
73         char * reply;
74         unsigned int maxlen = INITIAL_REPLY_LEN;
75         int again = 1;
76
77         reply = MALLOC(maxlen);
78
79         while (again) {
80                 if (!reply)
81                         return 1;
82
83                 c = reply;
84
85                 c += snprint_multipath_topology(c, reply + maxlen - c, mpp, 2);
86                 again = ((c - reply) == (maxlen - 1));
87
88                 REALLOC_REPLY(reply, again, maxlen);
89         }
90         *r = reply;
91         *len = (int)(c - reply + 1);
92         return 0;
93 }
94
95 int
96 show_maps_topology (char ** r, int * len, struct vectors * vecs)
97 {
98         int i;
99         struct multipath * mpp;
100         char * c;
101         char * reply;
102         unsigned int maxlen = INITIAL_REPLY_LEN;
103         int again = 1;
104  
105         get_path_layout(vecs->pathvec, 0);
106         reply = MALLOC(maxlen);
107
108         while (again) {
109                 if (!reply)
110                         return 1;
111
112                 c = reply;
113
114                 vector_foreach_slot(vecs->mpvec, mpp, i)
115                         c += snprint_multipath_topology(c, reply + maxlen - c,
116                                                         mpp, 2);
117
118                 again = ((c - reply) == (maxlen - 1));
119
120                 REALLOC_REPLY(reply, again, maxlen);
121         }
122         *r = reply;
123         *len = (int)(c - reply + 1);
124         return 0;
125 }
126
127 int
128 show_config (char ** r, int * len)
129 {
130         char * c;
131         char * reply;
132         unsigned int maxlen = INITIAL_REPLY_LEN;
133         int again = 1;
134
135         reply = MALLOC(maxlen);
136
137         while (again) {
138                 if (!reply)
139                         return 1;
140                 c = reply;
141                 c += snprint_defaults(c, reply + maxlen - c);
142                 again = ((c - reply) == maxlen);
143                 if (again) {
144                         reply = REALLOC(reply, maxlen * 2);
145                         if (!reply)
146                                 return 1;
147                         memset(reply + maxlen, 0, maxlen);
148                         maxlen *= 2;
149                         continue;
150                 }
151                 c += snprint_blacklist(c, reply + maxlen - c);
152                 again = ((c - reply) == maxlen);
153                 if (again) {
154                         reply = REALLOC(reply, maxlen * 2);
155                         if (!reply)
156                                 return 1;
157                         memset(reply + maxlen, 0, maxlen);
158                         maxlen *= 2;
159                         continue;
160                 }
161                 c += snprint_blacklist_except(c, reply + maxlen - c);
162                 again = ((c - reply) == maxlen);
163                 if (again) {
164                         reply = REALLOC(reply, maxlen * 2);
165                         if (!reply)
166                                 return 1;
167                         memset(reply + maxlen, 0, maxlen);
168                         maxlen *= 2;
169                         continue;
170                 }
171                 c += snprint_hwtable(c, reply + maxlen - c, conf->hwtable);
172                 again = ((c - reply) == maxlen);
173                 if (again) {
174                         reply = REALLOC(reply, maxlen * 2);
175                         if (!reply)
176                                 return 1;
177                         memset(reply + maxlen, 0, maxlen);
178                         maxlen *= 2;
179                         continue;
180                 }
181                 c += snprint_mptable(c, reply + maxlen - c, conf->mptable);
182                 again = ((c - reply) == maxlen);
183                 REALLOC_REPLY(reply, again, maxlen);
184         }
185         *r = reply;
186         *len = (int)(c - reply + 1);
187         return 0;
188 }
189
190 int
191 cli_list_config (void * v, char ** reply, int * len, void * data)
192 {
193         condlog(3, "list config (operator)");
194
195         return show_config(reply, len);
196 }
197
198 int
199 cli_list_paths (void * v, char ** reply, int * len, void * data)
200 {
201         struct vectors * vecs = (struct vectors *)data;
202
203         condlog(3, "list paths (operator)");
204
205         return show_paths(reply, len, vecs, PRINT_PATH_CHECKER);
206 }
207
208 int
209 cli_list_paths_fmt (void * v, char ** reply, int * len, void * data)
210 {
211         struct vectors * vecs = (struct vectors *)data;
212         char * fmt = get_keyparam(v, FMT);
213
214         condlog(3, "list paths (operator)");
215
216         return show_paths(reply, len, vecs, fmt);
217 }
218
219 int
220 cli_list_map_topology (void * v, char ** reply, int * len, void * data)
221 {
222         struct multipath * mpp;
223         struct vectors * vecs = (struct vectors *)data;
224         char * param = get_keyparam(v, MAP);
225         
226         get_path_layout(vecs->pathvec, 0);
227         mpp = find_mp_by_str(vecs->mpvec, param);
228
229         if (!mpp)
230                 return 1;
231
232         condlog(3, "list multipath %s (operator)", param);
233
234         return show_map_topology(reply, len, mpp);
235 }
236
237 int
238 cli_list_maps_topology (void * v, char ** reply, int * len, void * data)
239 {
240         struct vectors * vecs = (struct vectors *)data;
241
242         condlog(3, "list multipaths (operator)");
243
244         return show_maps_topology(reply, len, vecs);
245 }
246
247 int
248 cli_list_wildcards (void * v, char ** reply, int * len, void * data)
249 {
250         char * c;
251
252         *reply = MALLOC(INITIAL_REPLY_LEN);
253
254         if (!*reply)
255                 return 1;
256
257         c = *reply;
258         c += snprint_wildcards(c, INITIAL_REPLY_LEN);
259
260         *len = INITIAL_REPLY_LEN;
261         return 0;
262 }
263
264 int
265 show_status (char ** r, int *len, struct vectors * vecs)
266 {
267         char * c;
268         char * reply;
269
270         unsigned int maxlen = INITIAL_REPLY_LEN;
271         reply = MALLOC(maxlen);
272
273         if (!reply)
274                 return 1;
275
276         c = reply;
277         c += snprint_status(c, reply + maxlen - c, vecs);
278
279         *r = reply;
280         *len = (int)(c - reply + 1);
281         return 0;
282 }
283
284 int
285 show_daemon (char ** r, int *len)
286 {
287         char * c;
288         char * reply;
289
290         unsigned int maxlen = INITIAL_REPLY_LEN;
291         reply = MALLOC(maxlen);
292
293         if (!reply)
294                 return 1;
295
296         c = reply;
297         c += snprintf(c, INITIAL_REPLY_LEN, "%s\n", daemon_status());
298
299         *r = reply;
300         *len = (int)(c - reply + 1);
301         return 0;
302 }
303
304 int
305 show_maps (char ** r, int *len, struct vectors * vecs, char * style)
306 {
307         int i;
308         struct multipath * mpp;
309         char * c;
310         char * reply;
311         unsigned int maxlen = INITIAL_REPLY_LEN;
312         int again = 1;
313
314         get_multipath_layout(vecs->mpvec, 1);
315         reply = MALLOC(maxlen);
316
317         while (again) {
318                 if (!reply)
319                         return 1;
320
321                 c = reply;
322                 if (VECTOR_SIZE(vecs->mpvec) > 0)
323                         c += snprint_multipath_header(c, reply + maxlen - c,
324                                                       style);
325
326                 vector_foreach_slot(vecs->mpvec, mpp, i)
327                         c += snprint_multipath(c, reply + maxlen - c,
328                                                style, mpp);
329
330                 again = ((c - reply) == (maxlen - 1));
331
332                 REALLOC_REPLY(reply, again, maxlen);
333         }
334         *r = reply;
335         *len = (int)(c - reply + 1);
336         return 0;
337 }
338
339 int
340 cli_list_maps_fmt (void * v, char ** reply, int * len, void * data)
341 {
342         struct vectors * vecs = (struct vectors *)data;
343         char * fmt = get_keyparam(v, FMT);
344
345         condlog(3, "list maps (operator)");
346
347         return show_maps(reply, len, vecs, fmt);
348 }
349
350 int
351 cli_list_maps (void * v, char ** reply, int * len, void * data)
352 {
353         struct vectors * vecs = (struct vectors *)data;
354
355         condlog(3, "list maps (operator)");
356
357         return show_maps(reply, len, vecs, PRINT_MAP_NAMES);
358 }
359
360 int
361 cli_list_status (void * v, char ** reply, int * len, void * data)
362 {
363         struct vectors * vecs = (struct vectors *)data;
364
365         condlog(3, "list status (operator)");
366
367         return show_status(reply, len, vecs);
368 }
369
370 int
371 cli_list_maps_status (void * v, char ** reply, int * len, void * data)
372 {
373         struct vectors * vecs = (struct vectors *)data;
374
375         condlog(3, "list maps status (operator)");
376
377         return show_maps(reply, len, vecs, PRINT_MAP_STATUS);
378 }
379
380 int
381 cli_list_maps_stats (void * v, char ** reply, int * len, void * data)
382 {
383         struct vectors * vecs = (struct vectors *)data;
384
385         condlog(3, "list maps stats (operator)");
386
387         return show_maps(reply, len, vecs, PRINT_MAP_STATS);
388 }
389
390 int
391 cli_list_daemon (void * v, char ** reply, int * len, void * data)
392 {
393         condlog(3, "list daemon (operator)");
394
395         return show_daemon(reply, len);
396 }
397
398 int
399 cli_add_path (void * v, char ** reply, int * len, void * data)
400 {
401         struct vectors * vecs = (struct vectors *)data;
402         char * param = get_keyparam(v, PATH);
403         int r;
404
405         condlog(2, "%s: add path (operator)", param);
406
407         if (filter_devnode(conf->blist_devnode, conf->elist_devnode,
408             param) > 0 || (r = ev_add_path(param, vecs)) == 2) {
409                 *reply = strdup("blacklisted\n");
410                 *len = strlen(*reply) + 1;
411                 condlog(2, "%s: path blacklisted", param);
412                 return 0;
413         }
414         return r;
415 }
416
417 int
418 cli_del_path (void * v, char ** reply, int * len, void * data)
419 {
420         struct vectors * vecs = (struct vectors *)data;
421         char * param = get_keyparam(v, PATH);
422
423         condlog(2, "%s: remove path (operator)", param);
424
425         return ev_remove_path(param, vecs);
426 }
427
428 int
429 cli_add_map (void * v, char ** reply, int * len, void * data)
430 {
431         struct vectors * vecs = (struct vectors *)data;
432         char * param = get_keyparam(v, MAP);
433         int major, minor;
434         char dev_path[PATH_SIZE];
435         char *alias;
436         int rc;
437
438         condlog(2, "%s: add map (operator)", param);
439
440         if (filter_wwid(conf->blist_wwid, conf->elist_wwid, param) > 0) {
441                 *reply = strdup("blacklisted\n");
442                 *len = strlen(*reply) + 1;
443                 condlog(2, "%s: map blacklisted", param);
444                 return 0;
445         }
446         minor = dm_get_minor(param);
447         if (minor < 0) {
448                 condlog(2, "%s: not a device mapper table", param);
449                 return 0;
450         }
451         major = dm_get_major(param);
452         if (major < 0) {
453                 condlog(2, "%s: not a device mapper table", param);
454                 return 0;
455         }
456         sprintf(dev_path,"dm-%d", minor);
457         alias = dm_mapname(major, minor);
458         if (!alias) {
459                 condlog(2, "%s: mapname not found for %d:%d",
460                         param, major, minor);
461                 return 0;
462         }
463         rc = ev_add_map(dev_path, alias, vecs);
464         FREE(alias);
465         return rc;
466 }
467
468 int
469 cli_del_map (void * v, char ** reply, int * len, void * data)
470 {
471         struct vectors * vecs = (struct vectors *)data;
472         char * param = get_keyparam(v, MAP);
473         int major, minor;
474         char dev_path[PATH_SIZE];
475         char *alias;
476         int rc;
477
478         condlog(2, "%s: remove map (operator)", param);
479         minor = dm_get_minor(param);
480         if (minor < 0) {
481                 condlog(2, "%s: not a device mapper table", param);
482                 return 0;
483         }
484         major = dm_get_major(param);
485         if (major < 0) {
486                 condlog(2, "%s: not a device mapper table", param);
487                 return 0;
488         }
489         sprintf(dev_path,"dm-%d", minor);
490         alias = dm_mapname(major, minor);
491         if (!alias) {
492                 condlog(2, "%s: mapname not found for %d:%d",
493                         param, major, minor);
494                 return 0;
495         }
496         rc = ev_remove_map(param, alias, minor, vecs);
497         FREE(alias);
498         return rc;
499 }
500
501 int resize_map(struct multipath *mpp, unsigned long long size,
502                struct vectors * vecs)
503 {
504         char params[PARAMS_SIZE] = {0};
505
506         mpp->size = size;
507         update_mpp_paths(mpp, vecs->pathvec);
508         setup_map(mpp, params, PARAMS_SIZE);
509         mpp->action = ACT_RESIZE;
510         if (domap(mpp, params) <= 0) {
511                 condlog(0, "%s: failed to resize map : %s", mpp->alias,
512                         strerror(errno));
513                 return 1;
514         }
515         return 0;
516 }
517
518 int
519 cli_resize(void *v, char **reply, int *len, void *data)
520 {
521         struct vectors * vecs = (struct vectors *)data;
522         char * mapname = get_keyparam(v, MAP);
523         struct multipath *mpp;
524         int minor;
525         unsigned long long size;
526         struct pathgroup *pgp;
527         struct path *pp;
528
529         condlog(2, "%s: resize map (operator)", mapname);
530         if (sscanf(mapname, "dm-%d", &minor) == 1)
531                 mpp = find_mp_by_minor(vecs->mpvec, minor);
532         else
533                 mpp = find_mp_by_alias(vecs->mpvec, mapname);
534
535         if (!mpp) {
536                 condlog(0, "%s: invalid map name. cannot resize", mapname);
537                 return 1;
538         }
539
540         pgp = VECTOR_SLOT(mpp->pg, 0);
541         pp = VECTOR_SLOT(pgp->paths, 0);
542         if (sysfs_get_size(pp->sysdev, &size)) {
543                 condlog(0, "%s: couldn't get size for sysfs. cannot resize",
544                         mapname);
545                 return 1;
546         }
547         if (size == mpp->size) {
548                 condlog(0, "%s: map is still the same size (%llu)", mapname,
549                         mpp->size);
550                 return 0;
551         }
552         condlog(3, "%s old size is %llu, new size is %llu", mapname, mpp->size,
553                 size);
554
555         if (resize_map(mpp, size, vecs) != 0)
556                 return 1;
557
558         dm_lib_release();
559         setup_multipath(vecs, mpp);
560         sync_map_state(mpp);
561
562         return 0;
563 }
564
565 int
566 cli_restore_queueing(void *v, char **reply, int *len, void *data)
567 {
568         struct vectors * vecs = (struct vectors *)data;
569         char * mapname = get_keyparam(v, MAP);
570         struct multipath *mpp;
571         int minor;
572
573         condlog(2, "%s: restore map queueing (operator)", mapname);
574         if (sscanf(mapname, "dm-%d", &minor) == 1)
575                 mpp = find_mp_by_minor(vecs->mpvec, minor);
576         else
577                 mpp = find_mp_by_alias(vecs->mpvec, mapname);
578
579         if (!mpp) {
580                 condlog(0, "%s: invalid map name, cannot restore queueing", mapname);
581                 return 1;
582         }
583
584         if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF &&
585                         mpp->no_path_retry != NO_PATH_RETRY_FAIL) {
586                 dm_queue_if_no_path(mpp->alias, 1);
587                 if (mpp->nr_active > 0)
588                         mpp->retry_tick = 0;
589                 else
590                         mpp->retry_tick = mpp->no_path_retry * conf->checkint;
591         }
592         return 0;
593 }
594
595 int
596 cli_restore_all_queueing(void *v, char **reply, int *len, void *data)
597 {
598         struct vectors * vecs = (struct vectors *)data;
599         struct multipath *mpp;
600         int i;
601
602         condlog(2, "restore queueing (operator)");
603         vector_foreach_slot(vecs->mpvec, mpp, i) {
604                 if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF &&
605                     mpp->no_path_retry != NO_PATH_RETRY_FAIL) {
606                         dm_queue_if_no_path(mpp->alias, 1);
607                         if (mpp->nr_active > 0)
608                                 mpp->retry_tick = 0;
609                         else
610                                 mpp->retry_tick = mpp->no_path_retry * conf->checkint;
611                 }
612         }
613         return 0;
614 }
615
616 int
617 cli_disable_queueing(void *v, char **reply, int *len, void *data)
618 {
619         struct vectors * vecs = (struct vectors *)data;
620         char * mapname = get_keyparam(v, MAP);
621         struct multipath *mpp;
622         int minor;
623
624         condlog(2, "%s: disable map queueing (operator)", mapname);
625         if (sscanf(mapname, "dm-%d", &minor) == 1)
626                 mpp = find_mp_by_minor(vecs->mpvec, minor);
627         else
628                 mpp = find_mp_by_alias(vecs->mpvec, mapname);
629
630         if (!mpp) {
631                 condlog(0, "%s: invalid map name, cannot disable queueing", mapname);
632                 return 1;
633         }
634
635         mpp->retry_tick = 0;
636         dm_queue_if_no_path(mpp->alias, 0);
637         return 0;
638 }
639
640 int
641 cli_disable_all_queueing(void *v, char **reply, int *len, void *data)
642 {
643         struct vectors * vecs = (struct vectors *)data;
644         struct multipath *mpp;
645         int i;
646
647         condlog(2, "disable queueing (operator)");
648         vector_foreach_slot(vecs->mpvec, mpp, i) {
649                 mpp->retry_tick = 0;
650                 dm_queue_if_no_path(mpp->alias, 0);
651         }
652         return 0;
653 }
654
655 int
656 cli_switch_group(void * v, char ** reply, int * len, void * data)
657 {
658         char * mapname = get_keyparam(v, MAP);
659         int groupnum = atoi(get_keyparam(v, GROUP));
660
661         condlog(2, "%s: switch to path group #%i (operator)", mapname, groupnum);
662
663         return dm_switchgroup(mapname, groupnum);
664 }
665
666 int
667 cli_reconfigure(void * v, char ** reply, int * len, void * data)
668 {
669         struct vectors * vecs = (struct vectors *)data;
670
671         condlog(2, "reconfigure (operator)");
672
673         return reconfigure(vecs);
674 }
675
676 int
677 cli_suspend(void * v, char ** reply, int * len, void * data)
678 {
679         struct vectors * vecs = (struct vectors *)data;
680         char * param = get_keyparam(v, MAP);
681         int r = dm_simplecmd_noflush(DM_DEVICE_SUSPEND, param);
682
683         condlog(2, "%s: suspend (operator)", param);
684
685         if (!r) /* error */
686                 return 1;
687
688         struct multipath * mpp = find_mp_by_alias(vecs->mpvec, param);
689
690         if (!mpp)
691                 return 1;
692
693         dm_get_info(param, &mpp->dmi);
694         return 0;
695 }
696
697 int
698 cli_resume(void * v, char ** reply, int * len, void * data)
699 {
700         struct vectors * vecs = (struct vectors *)data;
701         char * param = get_keyparam(v, MAP);
702         int r = dm_simplecmd_noflush(DM_DEVICE_RESUME, param);
703
704         condlog(2, "%s: resume (operator)", param);
705
706         if (!r) /* error */
707                 return 1;
708
709         struct multipath * mpp = find_mp_by_alias(vecs->mpvec, param);
710
711         if (!mpp)
712                 return 1;
713
714         dm_get_info(param, &mpp->dmi);
715         return 0;
716 }
717
718 int
719 cli_reinstate(void * v, char ** reply, int * len, void * data)
720 {
721         struct vectors * vecs = (struct vectors *)data;
722         char * param = get_keyparam(v, PATH);
723         struct path * pp;
724
725         pp = find_path_by_dev(vecs->pathvec, param);
726
727         if (!pp)
728                  pp = find_path_by_devt(vecs->pathvec, param);
729
730         if (!pp || !pp->mpp || !pp->mpp->alias)
731                 return 1;
732
733         condlog(2, "%s: reinstate path %s (operator)",
734                 pp->mpp->alias, pp->dev_t);
735
736         checker_enable(&pp->checker);
737         return dm_reinstate_path(pp->mpp->alias, pp->dev_t);
738 }
739
740 int
741 cli_fail(void * v, char ** reply, int * len, void * data)
742 {
743         struct vectors * vecs = (struct vectors *)data;
744         char * param = get_keyparam(v, PATH);
745         struct path * pp;
746         int r;
747
748         pp = find_path_by_dev(vecs->pathvec, param);
749
750         if (!pp)
751                  pp = find_path_by_devt(vecs->pathvec, param);
752
753         if (!pp || !pp->mpp || !pp->mpp->alias)
754                 return 1;
755
756         condlog(2, "%s: fail path %s (operator)",
757                 pp->mpp->alias, pp->dev_t);
758
759         r = dm_fail_path(pp->mpp->alias, pp->dev_t);
760         /*
761          * Suspend path checking to avoid auto-reinstating the path
762          */
763         if (!r)
764                 checker_disable(&pp->checker);
765         return r;
766 }
767
768 int
769 show_blacklist (char ** r, int * len)
770 {
771         char *c = NULL;
772         char *reply = NULL;
773         unsigned int maxlen = INITIAL_REPLY_LEN;
774         int again = 1;
775
776         reply = MALLOC(maxlen);
777
778         while (again) {
779                 if (!reply)
780                         return 1;
781
782                 c = reply;
783                 c += snprint_blacklist_report(c, maxlen);
784                 again = ((c - reply) == maxlen);
785                 REALLOC_REPLY(reply, again, maxlen);
786         }
787
788         *r = reply;
789         *len = (int)(c - reply + 1);
790
791         return 0;
792 }
793
794 int
795 cli_list_blacklist (void * v, char ** reply, int * len, void * data)
796 {
797         condlog(3, "list blacklist (operator)");
798
799         return show_blacklist(reply, len);
800 }
801
802 int
803 show_devices (char ** r, int * len, struct vectors *vecs)
804 {
805         char *c = NULL;
806         char *reply = NULL;
807         unsigned int maxlen = INITIAL_REPLY_LEN;
808         int again = 1;
809
810         reply = MALLOC(maxlen);
811
812         while (again) {
813                 if (!reply)
814                         return 1;
815
816                 c = reply;
817                 c += snprint_devices(c, maxlen, vecs);
818                 again = ((c - reply) == maxlen);
819                 REALLOC_REPLY(reply, again, maxlen);
820         }
821
822         *r = reply;
823         *len = (int)(c - reply + 1);
824
825         return 0;
826 }
827
828 int
829 cli_list_devices (void * v, char ** reply, int * len, void * data)
830 {
831         struct vectors * vecs = (struct vectors *)data;
832
833         condlog(3, "list devices (operator)");
834
835         return show_devices(reply, len, vecs);
836 }
837
838 int
839 cli_quit (void * v, char ** reply, int * len, void * data)
840 {
841         return 0;
842 }
843
844 int
845 cli_shutdown (void * v, char ** reply, int * len, void * data)
846 {
847         condlog(3, "shutdown (operator)");
848
849         return exit_daemon(0);
850 }