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