hammer2 - SPAN protocol work
[dragonfly.git] / sbin / hammer2 / cmd_debug.c
1 /*
2  * Copyright (c) 2011-2012 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@dragonflybsd.org>
6  * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  * 3. Neither the name of The DragonFly Project nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific, prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35
36 #include "hammer2.h"
37
38 #define SHOW_TAB        2
39
40 static void shell_rcvmsg(hammer2_iocom_t *iocom, hammer2_msg_t *msg);
41 static void shell_ttymsg(hammer2_iocom_t *iocom);
42 static void hammer2_shell_parse(hammer2_iocom_t *iocom, hammer2_msg_t *msg);
43
44 /************************************************************************
45  *                                  SHELL                               *
46  ************************************************************************/
47
48 int
49 cmd_shell(const char *hostname)
50 {
51         struct hammer2_iocom iocom;
52         hammer2_msg_t *msg;
53         int fd;
54
55         /*
56          * Connect to the target
57          */
58         fd = hammer2_connect(hostname);
59         if (fd < 0)
60                 return 1;
61
62         /*
63          * Run the session.  The remote end transmits our prompt.
64          */
65         hammer2_iocom_init(&iocom, fd, 0, NULL, shell_rcvmsg, shell_ttymsg);
66         fcntl(0, F_SETFL, O_NONBLOCK);
67         printf("debug: connected\n");
68
69         msg = hammer2_msg_alloc(&iocom, 0, HAMMER2_DBG_SHELL);
70         hammer2_msg_write(&iocom, msg, NULL, NULL, NULL);
71         hammer2_iocom_core(&iocom);
72         fprintf(stderr, "debug: disconnected\n");
73         close(fd);
74         return 0;
75 }
76
77 /*
78  * Callback from hammer2_iocom_core() when messages might be present
79  * on the socket.
80  */
81 static
82 void
83 shell_rcvmsg(hammer2_iocom_t *iocom, hammer2_msg_t *msg)
84 {
85         switch(msg->any.head.cmd & HAMMER2_MSGF_CMDSWMASK) {
86         case HAMMER2_LNK_ERROR:
87         case HAMMER2_LNK_ERROR | HAMMER2_MSGF_REPLY:
88                 if (msg->any.head.error) {
89                         fprintf(stderr,
90                                 "Link Error: %d\n",
91                                 msg->any.head.error);
92                 } else {
93                         write(1, "debug> ", 7);
94                 }
95                 break;
96         case HAMMER2_DBG_SHELL:
97                 /*
98                  * We send the commands, not accept them.
99                  */
100                 hammer2_msg_reply(iocom, msg, HAMMER2_MSG_ERR_UNKNOWN);
101                 break;
102         case HAMMER2_DBG_SHELL | HAMMER2_MSGF_REPLY:
103                 /*
104                  * A reply from the remote is data we copy to stdout.
105                  */
106                 if (msg->aux_size) {
107                         msg->aux_data[msg->aux_size - 1] = 0;
108                         write(1, msg->aux_data, strlen(msg->aux_data));
109                 }
110                 break;
111         default:
112                 fprintf(stderr, "Unknown message: %08x\n",
113                         msg->any.head.cmd);
114                 assert((msg->any.head.cmd & HAMMER2_MSGF_REPLY) == 0);
115                 hammer2_msg_reply(iocom, msg, HAMMER2_MSG_ERR_UNKNOWN);
116                 break;
117         }
118 }
119
120 static
121 void
122 shell_ttymsg(hammer2_iocom_t *iocom)
123 {
124         hammer2_msg_t *msg;
125         char buf[256];
126         size_t len;
127
128         if (fgets(buf, sizeof(buf), stdin) != NULL) {
129                 len = strlen(buf);
130                 if (len && buf[len - 1] == '\n')
131                         buf[--len] = 0;
132                 ++len;
133                 msg = hammer2_msg_alloc(iocom, len, HAMMER2_DBG_SHELL);
134                 bcopy(buf, msg->aux_data, len);
135                 hammer2_msg_write(iocom, msg, NULL, NULL, NULL);
136         } else if (feof(stdin)) {
137                 /*
138                  * Set EOF flag without setting any error code for normal
139                  * EOF.
140                  */
141                 iocom->flags |= HAMMER2_IOCOMF_EOF;
142         } else {
143                 clearerr(stdin);
144         }
145 }
146
147 /*
148  * This is called from the master node to process a received debug
149  * shell command.  We process the command, outputting the results,
150  * then finish up by outputting another prompt.
151  */
152 void
153 hammer2_msg_dbg(hammer2_iocom_t *iocom, hammer2_msg_t *msg)
154 {
155         switch(msg->any.head.cmd & HAMMER2_MSGF_CMDSWMASK) {
156         case HAMMER2_DBG_SHELL:
157                 /*
158                  * This is a command which we must process.
159                  * When we are finished we generate a final reply.
160                  */
161                 if (msg->aux_data)
162                         msg->aux_data[msg->aux_size - 1] = 0;
163                 hammer2_shell_parse(iocom, msg);
164                 hammer2_msg_reply(iocom, msg, 0);
165                 break;
166         case HAMMER2_DBG_SHELL | HAMMER2_MSGF_REPLY:
167                 /*
168                  * A reply just prints out the string.  No newline is added
169                  * (it is expected to be embedded if desired).
170                  */
171                 if (msg->aux_data)
172                         msg->aux_data[msg->aux_size - 1] = 0;
173                 if (msg->aux_data)
174                         write(2, msg->aux_data, strlen(msg->aux_data));
175                 break;
176         default:
177                 hammer2_msg_reply(iocom, msg, HAMMER2_MSG_ERR_UNKNOWN);
178                 break;
179         }
180 }
181
182 static void
183 hammer2_shell_parse(hammer2_iocom_t *iocom, hammer2_msg_t *msg)
184 {
185         char *cmdbuf = msg->aux_data;
186         char *cmd = strsep(&cmdbuf, " \t");
187
188         if (cmd == NULL || *cmd == 0) {
189                 ;
190         } else if (strcmp(cmd, "span") == 0) {
191                 const char *hostname = strsep(&cmdbuf, " \t");
192                 pthread_t thread;
193                 int fd;
194
195                 /*
196                  * Connect to the target
197                  */
198                 if (hostname == NULL) {
199                         fd = -1;
200                 } else {
201                         fd = hammer2_connect(hostname);
202                 }
203
204                 /*
205                  * Start master service
206                  */
207                 if (fd < 0) {
208                         iocom_printf(iocom, 0, "Connection to %s failed\n",
209                                      hostname);
210                 } else {
211                         iocom_printf(iocom, 0, "Connected to %s\n", hostname);
212                         pthread_create(&thread, NULL,
213                                        master_service, (void *)(intptr_t)fd);
214                         /*pthread_join(thread, &res);*/
215                 }
216         } else if (strcmp(cmd, "help") == 0 || strcmp(cmd, "?") == 0) {
217                 iocom_printf(iocom, 0, "help        Command help\n");
218                 iocom_printf(iocom, 0, "span <host> Span to target host\n");
219         } else {
220                 iocom_printf(iocom, 0, "Unrecognized command: %s\n", cmd);
221         }
222 }
223
224 /*
225  * Returns text debug output to the original defined by (msg).  (msg) is
226  * not modified and stays intact.  We use a one-way message with REPLY set
227  * to distinguish between a debug command and debug terminal output.
228  *
229  * To prevent loops iocom_printf() can filter the message (cmd) related
230  * to the iocom_printf().  We filter out DBG messages.
231  */
232 void
233 iocom_printf(hammer2_iocom_t *iocom, uint32_t cmd, const char *ctl, ...)
234 {
235         hammer2_msg_t *rmsg;
236         va_list va;
237         char buf[1024];
238         size_t len;
239
240         if ((cmd & HAMMER2_MSGF_PROTOS) == HAMMER2_MSG_PROTO_DBG)
241                 return;
242
243         va_start(va, ctl);
244         vsnprintf(buf, sizeof(buf), ctl, va);
245         va_end(va);
246         len = strlen(buf) + 1;
247
248         rmsg = hammer2_msg_alloc(iocom, len, HAMMER2_DBG_SHELL |
249                                              HAMMER2_MSGF_REPLY);
250         bcopy(buf, rmsg->aux_data, len);
251
252         hammer2_msg_write(iocom, rmsg, NULL, NULL, NULL);
253 }
254
255 /************************************************************************
256  *                              DEBUGSPAN                               *
257  ************************************************************************
258  *
259  * Connect to the target manually (not via the cluster list embedded in
260  * a hammer2 filesystem) and initiate the SPAN protocol.
261  */
262 int
263 cmd_debugspan(const char *hostname)
264 {
265         pthread_t thread;
266         int fd;
267         void *res;
268
269         /*
270          * Connect to the target
271          */
272         fd = hammer2_connect(hostname);
273         if (fd < 0)
274                 return 1;
275
276         printf("debugspan: connected to %s, starting CONN/SPAN\n", hostname);
277         pthread_create(&thread, NULL, master_service, (void *)(intptr_t)fd);
278         pthread_join(thread, &res);
279         return(0);
280 }
281
282 /************************************************************************
283  *                                  SHOW                                *
284  ************************************************************************/
285
286 static void show_bref(int fd, int tab, int bi, hammer2_blockref_t *bref);
287 static void tabprintf(int tab, const char *ctl, ...);
288
289 int
290 cmd_show(const char *devpath)
291 {
292         hammer2_blockref_t broot;
293         int fd;
294
295         fd = open(devpath, O_RDONLY);
296         if (fd < 0) {
297                 perror("open");
298                 return 1;
299         }
300         bzero(&broot, sizeof(broot));
301         broot.type = HAMMER2_BREF_TYPE_VOLUME;
302         broot.data_off = 0 | HAMMER2_PBUFRADIX;
303         show_bref(fd, 0, 0, &broot);
304         close(fd);
305
306         return 0;
307 }
308
309 static void
310 show_bref(int fd, int tab, int bi, hammer2_blockref_t *bref)
311 {
312         hammer2_media_data_t media;
313         hammer2_blockref_t *bscan;
314         int bcount;
315         int i;
316         int didnl;
317         int namelen;
318         int obrace = 1;
319         size_t bytes;
320         const char *type_str;
321         char *str = NULL;
322
323         switch(bref->type) {
324         case HAMMER2_BREF_TYPE_EMPTY:
325                 type_str = "empty";
326                 break;
327         case HAMMER2_BREF_TYPE_INODE:
328                 type_str = "inode";
329                 break;
330         case HAMMER2_BREF_TYPE_INDIRECT:
331                 type_str = "indblk";
332                 break;
333         case HAMMER2_BREF_TYPE_DATA:
334                 type_str = "data";
335                 break;
336         case HAMMER2_BREF_TYPE_VOLUME:
337                 type_str = "volume";
338                 break;
339         default:
340                 type_str = "unknown";
341                 break;
342         }
343
344
345         tabprintf(tab, "%s.%-3d %016jx %016jx/%-2d mir=%016jx mod=%016jx ",
346                type_str, bi, (intmax_t)bref->data_off,
347                (intmax_t)bref->key, (intmax_t)bref->keybits,
348                (intmax_t)bref->mirror_tid, (intmax_t)bref->modify_tid);
349         tab += SHOW_TAB;
350
351         bytes = (size_t)1 << (bref->data_off & HAMMER2_OFF_MASK_RADIX);
352         if (bytes < HAMMER2_MINIOSIZE || bytes > sizeof(media)) {
353                 printf("(bad block size %zd)\n", bytes);
354                 return;
355         }
356         if (bref->type != HAMMER2_BREF_TYPE_DATA || VerboseOpt >= 1) {
357                 lseek(fd, bref->data_off & ~HAMMER2_OFF_MASK_RADIX, 0);
358                 if (read(fd, &media, bytes) != (ssize_t)bytes) {
359                         printf("(media read failed)\n");
360                         return;
361                 }
362         }
363
364         bscan = NULL;
365         bcount = 0;
366         didnl = 0;
367
368         switch(bref->type) {
369         case HAMMER2_BREF_TYPE_EMPTY:
370                 obrace = 0;
371                 break;
372         case HAMMER2_BREF_TYPE_INODE:
373                 printf("{\n");
374                 if (media.ipdata.op_flags & HAMMER2_OPFLAG_DIRECTDATA) {
375                         /* no blockrefs */
376                 } else {
377                         bscan = &media.ipdata.u.blockset.blockref[0];
378                         bcount = HAMMER2_SET_COUNT;
379                 }
380                 namelen = media.ipdata.name_len;
381                 if (namelen > HAMMER2_INODE_MAXNAME)
382                         namelen = 0;
383                 tabprintf(tab, "filename \"%*.*s\"\n",
384                           namelen, namelen, media.ipdata.filename);
385                 tabprintf(tab, "version  %d\n", media.ipdata.version);
386                 tabprintf(tab, "uflags   0x%08x\n",
387                           media.ipdata.uflags);
388                 if (media.ipdata.rmajor || media.ipdata.rminor) {
389                         tabprintf(tab, "rmajor   %d\n",
390                                   media.ipdata.rmajor);
391                         tabprintf(tab, "rminor   %d\n",
392                                   media.ipdata.rminor);
393                 }
394                 tabprintf(tab, "ctime    %s\n",
395                           hammer2_time64_to_str(media.ipdata.ctime, &str));
396                 tabprintf(tab, "mtime    %s\n",
397                           hammer2_time64_to_str(media.ipdata.mtime, &str));
398                 tabprintf(tab, "atime    %s\n",
399                           hammer2_time64_to_str(media.ipdata.atime, &str));
400                 tabprintf(tab, "btime    %s\n",
401                           hammer2_time64_to_str(media.ipdata.btime, &str));
402                 tabprintf(tab, "uid      %s\n",
403                           hammer2_uuid_to_str(&media.ipdata.uid, &str));
404                 tabprintf(tab, "gid      %s\n",
405                           hammer2_uuid_to_str(&media.ipdata.gid, &str));
406                 tabprintf(tab, "type     %s\n",
407                           hammer2_iptype_to_str(media.ipdata.type));
408                 tabprintf(tab, "opflgs   0x%02x\n",
409                           media.ipdata.op_flags);
410                 tabprintf(tab, "capflgs  0x%04x\n",
411                           media.ipdata.cap_flags);
412                 tabprintf(tab, "mode     %-7o\n",
413                           media.ipdata.mode);
414                 tabprintf(tab, "inum     0x%016jx\n",
415                           media.ipdata.inum);
416                 tabprintf(tab, "size     %ju\n",
417                           (uintmax_t)media.ipdata.size);
418                 tabprintf(tab, "nlinks   %ju\n",
419                           (uintmax_t)media.ipdata.nlinks);
420                 tabprintf(tab, "iparent  0x%016jx\n",
421                           (uintmax_t)media.ipdata.iparent);
422                 tabprintf(tab, "name_key 0x%016jx\n",
423                           (uintmax_t)media.ipdata.name_key);
424                 tabprintf(tab, "name_len %u\n",
425                           media.ipdata.name_len);
426                 tabprintf(tab, "ncopies  %u\n",
427                           media.ipdata.ncopies);
428                 tabprintf(tab, "compalg  %u\n",
429                           media.ipdata.comp_algo);
430                 if (media.ipdata.op_flags & HAMMER2_OPFLAG_PFSROOT) {
431                         tabprintf(tab, "pfs_type %u (%s)\n",
432                                   media.ipdata.pfs_type,
433                                   hammer2_pfstype_to_str(media.ipdata.pfs_type));
434                         tabprintf(tab, "pfs_inum 0x%016jx\n",
435                                   (uintmax_t)media.ipdata.pfs_inum);
436                         tabprintf(tab, "pfs_clid %s\n",
437                                   hammer2_uuid_to_str(&media.ipdata.pfs_clid,
438                                                       &str));
439                         tabprintf(tab, "pfs_fsid %s\n",
440                                   hammer2_uuid_to_str(&media.ipdata.pfs_fsid,
441                                                       &str));
442                 }
443                 tabprintf(tab, "data_quota  %ju\n",
444                           (uintmax_t)media.ipdata.data_quota);
445                 tabprintf(tab, "data_count  %ju\n",
446                           (uintmax_t)media.ipdata.data_count);
447                 tabprintf(tab, "inode_quota %ju\n",
448                           (uintmax_t)media.ipdata.inode_quota);
449                 tabprintf(tab, "inode_count %ju\n",
450                           (uintmax_t)media.ipdata.inode_count);
451                 tabprintf(tab, "attr_tid    0x%016jx\n",
452                           (uintmax_t)media.ipdata.attr_tid);
453                 if (media.ipdata.type == HAMMER2_OBJTYPE_DIRECTORY) {
454                         tabprintf(tab, "dirent_tid  %016jx\n",
455                                   (uintmax_t)media.ipdata.dirent_tid);
456                 }
457                 break;
458         case HAMMER2_BREF_TYPE_INDIRECT:
459                 bscan = &media.npdata.blockref[0];
460                 bcount = bytes / sizeof(hammer2_blockref_t);
461                 didnl = 1;
462                 printf("{\n");
463                 break;
464         case HAMMER2_BREF_TYPE_DATA:
465                 if (VerboseOpt >= 2) {
466                         printf("{\n");
467                 } else {
468                         printf("\n");
469                         obrace = 0;
470                 }
471                 break;
472         case HAMMER2_BREF_TYPE_VOLUME:
473                 bscan = &media.voldata.sroot_blockset.blockref[0];
474                 bcount = HAMMER2_SET_COUNT;
475                 printf("{\n");
476                 break;
477         default:
478                 break;
479         }
480         if (str)
481                 free(str);
482         for (i = 0; i < bcount; ++i) {
483                 if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) {
484                         if (didnl == 0) {
485                                 printf("\n");
486                                 didnl = 1;
487                         }
488                         show_bref(fd, tab, i, &bscan[i]);
489                 }
490         }
491         tab -= SHOW_TAB;
492         if (obrace) {
493                 if (bref->type == HAMMER2_BREF_TYPE_INODE)
494                         tabprintf(tab, "} (%s.%d, \"%s\")\n",
495                                   type_str, bi, media.ipdata.filename);
496                 else
497                         tabprintf(tab, "} (%s.%d)\n", type_str,bi);
498         }
499 }
500
501 static
502 void
503 tabprintf(int tab, const char *ctl, ...)
504 {
505         va_list va;
506
507         printf("%*.*s", tab, tab, "");
508         va_start(va, ctl);
509         vprintf(ctl, va);
510         va_end(va);
511 }