Merge branches 'hammer2' and 'master' of ssh://crater.dragonflybsd.org/repository...
[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 static void debug_recv(hammer2_iocom_t *iocom);
39 static void debug_send(hammer2_iocom_t *iocom);
40 static void debug_tty(hammer2_iocom_t *iocom);
41 static void hammer2_debug_parse(hammer2_msg_t *msg, char *cmdbuf);
42
43 int
44 cmd_debug(const char *hostname)
45 {
46         struct sockaddr_in lsin;
47         struct hammer2_iocom iocom;
48         hammer2_msg_t *msg;
49         struct hostent *hen;
50         int fd;
51
52         /*
53          * Acquire socket and set options
54          */
55         if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
56                 fprintf(stderr, "cmd_debug: socket(): %s\n",
57                         strerror(errno));
58                 return 1;
59         }
60
61         /*
62          * Connect to the target
63          */
64         bzero(&lsin, sizeof(lsin));
65         lsin.sin_family = AF_INET;
66         lsin.sin_addr.s_addr = 0;
67         lsin.sin_port = htons(HAMMER2_LISTEN_PORT);
68
69         if (hostname) {
70                 hen = gethostbyname2(hostname, AF_INET);
71                 if (hen == NULL) {
72                         if (inet_pton(AF_INET, hostname, &lsin.sin_addr) != 1) {
73                                 fprintf(stderr,
74                                         "Cannot resolve %s\n", hostname);
75                                 return 1;
76                         }
77                 } else {
78                         bcopy(hen->h_addr, &lsin.sin_addr, hen->h_length);
79                 }
80         }
81         if (connect(fd, (struct sockaddr *)&lsin, sizeof(lsin)) < 0) {
82                 close(fd);
83                 fprintf(stderr, "debug: connect failed: %s\n",
84                         strerror(errno));
85                 return 0;
86         }
87
88         /*
89          * Run the session.  The remote end transmits our prompt.
90          */
91         hammer2_iocom_init(&iocom, fd, 0);
92         printf("debug: connected\n");
93
94         msg = hammer2_allocmsg(&iocom, HAMMER2_DBG_SHELL, 0);
95         hammer2_ioq_write(msg);
96
97         hammer2_iocom_core(&iocom, debug_recv, debug_send, debug_tty);
98         fprintf(stderr, "debug: disconnected\n");
99         close(fd);
100         return 0;
101 }
102
103 /*
104  * Callback from hammer2_iocom_core() when messages might be present
105  * on the socket.
106  */
107 static
108 void
109 debug_recv(hammer2_iocom_t *iocom)
110 {
111         hammer2_msg_t *msg;
112
113         while ((iocom->flags & HAMMER2_IOCOMF_EOF) == 0 &&
114                (msg = hammer2_ioq_read(iocom)) != NULL) {
115
116                 switch(msg->any.head.cmd & HAMMER2_MSGF_CMDSWMASK) {
117                 case HAMMER2_LNK_ERROR:
118                         fprintf(stderr, "Link Error: %d\n",
119                                 msg->any.head.error);
120                         break;
121                 case HAMMER2_DBG_SHELL:
122                         /*
123                          * We send the commands, not accept them.
124                          */
125                         hammer2_replymsg(msg, HAMMER2_MSG_ERR_UNKNOWN);
126                         hammer2_freemsg(msg);
127                         break;
128                 case HAMMER2_DBG_SHELL | HAMMER2_MSGF_REPLY:
129                         /*
130                          * A reply from the remote is data we copy to stdout.
131                          */
132                         if (msg->aux_size) {
133                                 msg->aux_data[msg->aux_size - 1] = 0;
134                                 write(1, msg->aux_data, strlen(msg->aux_data));
135                         } else {
136                                 write(1, "debug> ", 7);
137                         }
138                         hammer2_freemsg(msg);
139                         break;
140                 default:
141                         assert((msg->any.head.cmd & HAMMER2_MSGF_REPLY) == 0);
142                         fprintf(stderr, "Unknown message: %08x\n",
143                                 msg->any.head.cmd);
144                         hammer2_replymsg(msg, HAMMER2_MSG_ERR_UNKNOWN);
145                         break;
146                 }
147         }
148         if (iocom->ioq_rx.error) {
149                 fprintf(stderr, "node_master_recv: comm error %d\n",
150                         iocom->ioq_rx.error);
151         }
152 }
153
154 /*
155  * Callback from hammer2_iocom_core() when messages might be transmittable
156  * to the socket.
157  */
158 static
159 void
160 debug_send(hammer2_iocom_t *iocom)
161 {
162         hammer2_iocom_flush(iocom);
163 }
164
165 static
166 void
167 debug_tty(hammer2_iocom_t *iocom)
168 {
169         hammer2_msg_t *msg;
170         char buf[256];
171         size_t len;
172
173         if (fgets(buf, sizeof(buf), stdin) != NULL) {
174                 len = strlen(buf);
175                 if (len && buf[len - 1] == '\n')
176                         buf[--len] = 0;
177                 ++len;
178                 msg = hammer2_allocmsg(iocom, HAMMER2_DBG_SHELL, len);
179                 bcopy(buf, msg->aux_data, len);
180                 hammer2_ioq_write(msg);
181         } else {
182                 /*
183                  * Set EOF flag without setting any error code for normal
184                  * EOF.
185                  */
186                 iocom->flags |= HAMMER2_IOCOMF_EOF;
187         }
188 }
189
190 /*
191  * This is called from the master node to process a received debug
192  * shell command.  We process the command, outputting the results,
193  * then finish up by outputting another prompt.
194  */
195 void
196 hammer2_debug_remote(hammer2_msg_t *msg)
197 {
198         /* hammer2_iocom_t *iocom = msg->iocom; */
199
200         if (msg->aux_data)
201                 msg->aux_data[msg->aux_size - 1] = 0;
202         if (msg->any.head.cmd & HAMMER2_MSGF_REPLY) {
203                 /*
204                  * A reply just prints out the string.  No newline is added
205                  * (it is expected to be embedded if desired).
206                  */
207                 if (msg->aux_data)
208                         write(2, msg->aux_data, strlen(msg->aux_data));
209                 hammer2_freemsg(msg);
210         } else {
211                 /*
212                  * Otherwise this is a command which we must process.
213                  * When we are finished we generate a final reply.
214                  */
215                 hammer2_debug_parse(msg, msg->aux_data);
216                 hammer2_replymsg(msg, 0);
217         }
218 }
219
220 static void
221 hammer2_debug_parse(hammer2_msg_t *msg, char *cmdbuf)
222 {
223         /* hammer2_iocom_t *iocom = msg->iocom; */
224         char *cmd = strsep(&cmdbuf, " \t");
225
226         if (cmd == NULL || *cmd == 0) {
227                 ;
228         } else if (strcmp(cmd, "help") == 0 || strcmp(cmd, "?") == 0) {
229                 msg_printf(msg, "help        Command help\n");
230         } else {
231                 msg_printf(msg, "Unrecognized command: %s\n", cmd);
232         }
233 }
234
235 /*
236  * Returns text debug output to the original defined by (msg).  (msg) is
237  * not modified and stays intact.
238  */
239 void
240 msg_printf(hammer2_msg_t *msg, const char *ctl, ...)
241 {
242         /* hammer2_iocom_t *iocom = msg->iocom; */
243         hammer2_msg_t *rmsg;
244         va_list va;
245         char buf[1024];
246         size_t len;
247
248         va_start(va, ctl);
249         vsnprintf(buf, sizeof(buf), ctl, va);
250         va_end(va);
251         len = strlen(buf) + 1;
252
253         rmsg = hammer2_allocreply(msg, HAMMER2_DBG_SHELL, len);
254         bcopy(buf, rmsg->aux_data, len);
255
256         hammer2_ioq_write(rmsg);
257 }