Merge branches 'hammer2' and 'master' of ssh://crater.dragonflybsd.org/repository...
[dragonfly.git] / sbin / hammer2 / cmd_service.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 *master_accept(void *data);
39 static void *master_service(void *data);
40 static void master_auth_rx(hammer2_iocom_t *iocom);
41 static void master_auth_tx(hammer2_iocom_t *iocom);
42 static void master_link_rx(hammer2_iocom_t *iocom);
43 static void master_link_tx(hammer2_iocom_t *iocom);
44
45 static void hammer2_lnk_span(hammer2_iocom_t *iocom, hammer2_msg_t *msg);
46
47 /*
48  * Start-up the master listener daemon for the machine.
49  *
50  * The master listener serves as a rendezvous point in the cluster, accepting
51  * connections, performing registrations and authentications, maintaining
52  * the spanning tree, and keeping track of message state so disconnects can
53  * be handled properly.
54  *
55  * Once authenticated only low-level messaging protocols (which includes
56  * tracking persistent messages) are handled by this daemon.  This daemon
57  * does not run the higher level quorum or locking protocols.
58  *
59  * This daemon can also be told to maintain connections to other nodes,
60  * forming a messaging backbone, which in turn allows PFS's (if desired) to
61  * simply connect to the master daemon via localhost if desired.
62  * Backbones are specified via /etc/hammer2.conf.
63  */
64 int
65 cmd_service(void)
66 {
67         struct sockaddr_in lsin;
68         int on;
69         int lfd;
70
71         /*
72          * Acquire socket and set options
73          */
74         if ((lfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
75                 fprintf(stderr, "master_listen: socket(): %s\n",
76                         strerror(errno));
77                 return 1;
78         }
79         on = 1;
80         setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
81
82         /*
83          * Setup listen port and try to bind.  If the bind fails we assume
84          * that a master listener process is already running and silently
85          * fail.
86          */
87         bzero(&lsin, sizeof(lsin));
88         lsin.sin_family = AF_INET;
89         lsin.sin_addr.s_addr = INADDR_ANY;
90         lsin.sin_port = htons(HAMMER2_LISTEN_PORT);
91         if (bind(lfd, (struct sockaddr *)&lsin, sizeof(lsin)) < 0) {
92                 close(lfd);
93                 if (QuietOpt == 0) {
94                         fprintf(stderr,
95                                 "master listen: daemon already running\n");
96                 }
97                 return 0;
98         }
99         if (QuietOpt == 0)
100                 fprintf(stderr, "master listen: startup\n");
101         listen(lfd, 50);
102
103         /*
104          * Fork and disconnect the controlling terminal and parent process,
105          * executing the specified function as a pthread.
106          *
107          * Returns to the original process which can then continue running.
108          * In debug mode this call will create the pthread without forking
109          * and set NormalExit to 0, instead of fork.
110          */
111         hammer2_demon(master_accept, (void *)(intptr_t)lfd);
112         if (NormalExit)
113                 close(lfd);
114         return 0;
115 }
116
117 /*
118  * Master listen/accept thread.  Accept connections on the master socket,
119  * starting a pthread for each one.
120  */
121 static
122 void *
123 master_accept(void *data)
124 {
125         struct sockaddr_in asin;
126         socklen_t alen;
127         pthread_t thread;
128         int lfd = (int)(intptr_t)data;
129         int fd;
130
131         /*
132          * Nobody waits for us
133          */
134         setproctitle("hammer2 master listen");
135         pthread_detach(pthread_self());
136
137         /*
138          * Accept connections and create pthreads to handle them after
139          * validating the IP.
140          */
141         for (;;) {
142                 alen = sizeof(asin);
143                 fd = accept(lfd, (struct sockaddr *)&asin, &alen);
144                 if (fd < 0) {
145                         if (errno == EINTR)
146                                 continue;
147                         break;
148                 }
149                 thread = NULL;
150                 fprintf(stderr, "master_accept: accept fd %d\n", fd);
151                 pthread_create(&thread, NULL,
152                                master_service, (void *)(intptr_t)fd);
153         }
154         return (NULL);
155 }
156
157 /*
158  * Service an accepted connection (runs as a pthread)
159  */
160 static
161 void *
162 master_service(void *data)
163 {
164         hammer2_iocom_t iocom;
165         int fd;
166
167         fd = (int)(intptr_t)data;
168         hammer2_iocom_init(&iocom, fd, -1);
169         hammer2_iocom_core(&iocom, master_auth_rx, master_auth_tx, NULL);
170
171         fprintf(stderr,
172                 "iocom on fd %d terminated error rx=%d, tx=%d\n",
173                 fd, iocom.ioq_rx.error, iocom.ioq_tx.error);
174         close(fd);
175
176         return (NULL);
177 }
178
179 /************************************************************************
180  *                          AUTHENTICATION                              *
181  ************************************************************************
182  *
183  * Callback via hammer2_iocom_core().
184  *
185  * Additional messaging-based authentication must occur before normal
186  * message operation.  The connection has already been encrypted at
187  * this point.
188  */
189 static
190 void
191 master_auth_rx(hammer2_iocom_t *iocom __unused)
192 {
193         printf("AUTHRX\n");
194         iocom->recvmsg_callback = master_link_rx;
195         iocom->sendmsg_callback = master_link_tx;
196 }
197
198 static
199 void
200 master_auth_tx(hammer2_iocom_t *iocom __unused)
201 {
202         printf("AUTHTX\n");
203         iocom->recvmsg_callback = master_link_rx;
204         iocom->sendmsg_callback = master_link_tx;
205 }
206
207 /************************************************************************
208  *                      POST-AUTHENTICATION SERVICE MSGS                *
209  ************************************************************************
210  *
211  * Callback via hammer2_iocom_core().
212  */
213 static
214 void
215 master_link_rx(hammer2_iocom_t *iocom)
216 {
217         hammer2_msg_t *msg;
218         uint32_t cmd;
219
220         while ((iocom->flags & HAMMER2_IOCOMF_EOF) == 0 &&
221                (msg = hammer2_ioq_read(iocom)) != NULL) {
222                 /*
223                  * Switch on the transactional cmd, that is the original
224                  * msg->any.head.cmd that opened the transaction.  The actual
225                  * msg might be different.  The original msg cannot have
226                  * REPLY set by definition (but of course the currenet msg
227                  * might), so we don't bother with case statements for REPLY
228                  * for command sequences we expet to be transactional.
229                  *
230                  * Non-transactional one-off messages, on the otherhand,
231                  * might have REPLY set.
232                  */
233                 if (msg->state) {
234                         cmd = msg->state->msg->any.head.cmd;
235                         fprintf(stderr,
236                                 "MSGRX persist=%08x cmd=%08x error %d\n",
237                                 cmd, msg->any.head.cmd, msg->any.head.error);
238                 } else {
239                         cmd = msg->any.head.cmd;
240                         fprintf(stderr,
241                                 "MSGRX persist=-------- cmd=%08x error %d\n",
242                                 cmd, msg->any.head.error);
243                 }
244
245                 switch(cmd & HAMMER2_MSGF_CMDSWMASK) {
246                 case HAMMER2_LNK_ERROR:
247                         /*
248                          * A non-transactional error is formulated when
249                          * the socket or pipe disconnects.  Ignore it.
250                          */
251                         break;
252                 case HAMMER2_LNK_SPAN:
253                         /*
254                          * Messages related to the LNK_SPAN transaction.
255                          */
256                         hammer2_lnk_span(iocom, msg);
257                         break;
258                 case HAMMER2_DBG_SHELL:
259                 case HAMMER2_DBG_SHELL | HAMMER2_MSGF_REPLY:
260                         /*
261                          * Non-transactional DBG messages.
262                          */
263                         hammer2_shell_remote(iocom, msg);
264                         break;
265                 default:
266                         hammer2_msg_reply(iocom, msg, HAMMER2_MSG_ERR_UNKNOWN);
267                         break;
268                 }
269                 hammer2_state_cleanuprx(iocom, msg);
270         }
271         if (iocom->ioq_rx.error) {
272                 fprintf(stderr,
273                         "master_recv: comm error %d\n",
274                         iocom->ioq_rx.error);
275         }
276 }
277
278 /*
279  * Callback from hammer2_iocom_core() when messages might be transmittable
280  * to the socket.
281  */
282 static
283 void
284 master_link_tx(hammer2_iocom_t *iocom)
285 {
286         hammer2_iocom_flush(iocom);
287 }
288
289 /*
290  * Receive a message which is part of a LNK_SPAN transaction.  Keep in
291  * mind that only the original CREATE is utilizing the lnk_span message
292  * header.
293  *
294  * We will get called for CREATE, DELETE, and intermediate states (including
295  * errors), and in particular we will get called with an error if the link
296  * is lost in the middle of the transaction.
297  */
298 static
299 void
300 hammer2_lnk_span(hammer2_iocom_t *iocom __unused, hammer2_msg_t *msg)
301 {
302         char *alloc = NULL;
303
304         switch(msg->any.head.cmd & HAMMER2_MSGF_TRANSMASK) {
305         case HAMMER2_LNK_SPAN | HAMMER2_MSGF_CREATE:
306                 fprintf(stderr,
307                         "LNK_SPAN: %s/%s\n",
308                         hammer2_uuid_to_str(&msg->any.lnk_span.pfs_id, &alloc),
309                         msg->any.lnk_span.label);
310                 free(alloc);
311                 break;
312         case HAMMER2_LNK_ERROR | HAMMER2_MSGF_DELETE:
313                 fprintf(stderr, "LNK_SPAN: Terminated with error\n");
314                 break;
315         default:
316                 fprintf(stderr,
317                         "LNK_SPAN: Unknown msg %08x\n", msg->any.head.cmd);
318                 break;
319         }
320 }