Bump version to 0.3.5next
[archipelago] / xseg / peers / user / mapperd.c
1 /*
2  * Copyright 2012 GRNET S.A. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or
5  * without modification, are permitted provided that the following
6  * conditions are met:
7  *
8  *   1. Redistributions of source code must retain the above
9  *      copyright notice, this list of conditions and the following
10  *      disclaimer.
11  *   2. Redistributions in binary form must reproduce the above
12  *      copyright notice, this list of conditions and the following
13  *      disclaimer in the documentation and/or other materials
14  *      provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  *
29  * The views and conclusions contained in the software and
30  * documentation are those of the authors and should not be
31  * interpreted as representing official policies, either expressed
32  * or implied, of GRNET S.A.
33  */
34
35 /*
36  * The Mapper
37  */
38
39 #define _GNU_SOURCE
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <unistd.h>
45 #include <string.h>
46 #include <fcntl.h>
47 #include <errno.h>
48 #include <aio.h>
49 #include <signal.h>
50 #include <limits.h>
51 #include <xseg/xseg.h>
52 #include <pthread.h>
53
54 #include <xseg/protocol.h>
55
56 #include "common.h"  /* Please fix me */
57
58 #define MAX_PATH_SIZE 255
59 #define MAX_FILENAME_SIZE 255
60
61 #define DEFAULT_NR_OPS 16
62
63 #define MAPPER_SANITY_CHECKS 1
64
65 /*
66  * Globals, holding command-line arguments
67  */
68 long cmdline_mportno = -1;
69 long cmdline_bportno = -1;
70 char *cmdline_xseg_spec = NULL;
71 long cmdline_nr_ops = DEFAULT_NR_OPS;
72
73 struct mapperd {
74         struct xseg *xseg;
75         struct xseg_port *mport;
76         uint32_t mportno, bportno;
77 };
78
79 static int usage(char *argv0)
80 {
81         fprintf(stderr,
82                 "Usage: %s [-p MAPPERD_PORT]"
83                         "[-b BLOCKD_POART] [-g XSEG_SPEC] [-n NR_OPS]\n\n"
84                 "where:\n"
85                 "\tMAPPERD_PORT: xseg port to listen for requests on\n"
86                 "\tBLOCKD_PORT: xseg port where blockd/filed/sosd lives\n"
87                 "\tXSEG_SPEC: xseg spec as 'type:name:nr_ports:nr_requests:"
88                         "request_size:extra_size:page_shift'\n"
89                 "\tNR_OPS: number of outstanding xseg requests\n",
90                 argv0);
91
92         return 1;
93 }
94
95 static int safe_atoi(char *s)
96 {
97         long l;
98         char *endp;
99
100         l = strtol(s, &endp, 10);
101         if (s != endp && *endp == '\0')
102                 return l;
103         else
104                 return -1;
105 }
106
107 static void parse_cmdline(int argc, char **argv)
108 {
109         for (;;) {
110                 int c;
111
112                 opterr = 0;
113                 c = getopt(argc, argv, "+:hp:b:n:g:");
114                 if (c == -1)
115                         break;
116                 
117                 switch(c) {
118                         case '?':
119                                 perr(PFE, 0, "Unknown option: -%c", optopt);
120                                 break;
121                         case ':':
122                                 perr(PFE, 0, "Option -%c requires an argument",
123                                         optopt);
124                                 break;
125                         case 'h':
126                                 usage(argv[0]);
127                                 exit(0);
128                                 break;
129                         case 'p':
130                                 cmdline_mportno = safe_atoi(optarg);
131                                 break;
132                         case 'b':
133                                 cmdline_bportno = safe_atoi(optarg);
134                                 break;
135                         case 'n':
136                                 cmdline_nr_ops = safe_atoi(optarg);
137                                 break;
138                         case 'g':
139                                 /* FIXME: Max length of spec? strdup, eww */
140                                 cmdline_xseg_spec = strdup(optarg);
141                                 if (!cmdline_xseg_spec)
142                                         perr(PFE, 0, "out of memory");
143                                 break;
144                 }
145         }
146
147         argc -= optind;
148         argv += optind;
149
150         /* Sanity check for all arguments */
151         if (cmdline_mportno < 0)
152                 perr(PFE, 0, "no or invalid port specified for mapperd");
153         if (cmdline_bportno != -1)
154                 perr(PFE, 0, "This is a no-op 1-1 mapper. Cannot specify a blocker port");
155                 //no or invalid port specified for blockd/filed/sosd");
156         if (cmdline_nr_ops < 1)
157                 perr(PFE, 0, "specified outstanding request count is invalid");
158         if (!cmdline_xseg_spec)
159                 perr(PFE, 0, "xseg specification is mandatory");
160
161         if (argc)
162                 perr(PFE, 0, "Non-option arguments specified on command line");
163 }
164
165 static struct xseg *join_or_create(char *spec)
166 {
167         struct xseg_config config;
168         struct xseg *xseg;
169
170         (void)xseg_parse_spec(spec, &config);
171         xseg = xseg_join(config.type, config.name, "posix", NULL);
172         if (xseg)
173                 return xseg;
174
175         (void)xseg_create(&config);
176         return xseg_join(config.type, config.name, "posix", NULL);
177 }
178
179 static int mapperd_loop(struct mapperd *mapperd)
180 {
181         int ret;
182         struct xseg_request *xreq;
183         struct xseg *xseg = mapperd->xseg;
184         uint32_t mportno = mapperd->mportno;
185         char *target, buf[MAX_FILENAME_SIZE];
186         xport p;
187
188         always_assert(xseg);
189
190         for (;;) {
191                 ret = xseg_prepare_wait(xseg, mportno);
192                 always_assert(ret == 0);
193
194                 xreq = xseg_accept(xseg, mportno, 0);
195                 if (xreq) {
196                         xseg_cancel_wait(xseg, mportno);
197                         /*
198                          * Construct a 1-1 reply immediately, make sure it fits
199                          * Verify the initiator has allocated enough space for
200                          * the reply and the target name fits in the map reply.
201                          */
202                         size_t s = sizeof(struct xseg_reply_map) +
203                                 2 * sizeof(struct xseg_reply_map_scatterlist);
204                         target = xseg_get_target(xseg, xreq);
205                         strncpy(buf, target, xreq->targetlen);
206                         xseg_resize_request(xseg, xreq, xreq->targetlen, s);
207                         target = xseg_get_target(xseg, xreq);
208                         strncpy(target, buf, xreq->targetlen);
209
210                         struct xseg_reply_map *mreply = (void *)xseg_get_data(xseg, xreq);
211                         mreply->cnt = 2;
212                         mreply->segs[0].offset = xreq->offset;
213                         mreply->segs[0].size = xreq->size/2;
214                         /* FIXME: strlcpy() would work nicely here */
215                         strncpy(mreply->segs[0].target, target, xreq->targetlen);
216                         mreply->segs[0].target[xreq->targetlen] = '_';
217                         mreply->segs[0].target[xreq->targetlen + 1] = '1';
218                         mreply->segs[0].target[xreq->targetlen + 2] = '\0';
219                         
220                         mreply->segs[1].offset = xreq->offset;
221                         mreply->segs[1].size = xreq->size/2;
222                         /* FIXME: strlcpy() would work nicely here */
223                         strncpy(mreply->segs[1].target, target, xreq->targetlen);
224                         mreply->segs[1].target[xreq->targetlen] = '_';
225                         mreply->segs[1].target[xreq->targetlen + 1] = '2';
226                         mreply->segs[1].target[xreq->targetlen + 2] = '\0';
227
228                         /* Respond to the initiator, signal the source port */
229 //                      perr(PI, 0, "completed io");
230                         xreq->state |= XS_SERVED;
231                         p = xseg_respond(xseg, xreq, mportno, X_ALLOC);
232                         ret = xseg_signal(xseg, p);
233 //                      always_assert(ret == 1);
234                 } else {
235                         /*
236                          * If things are OK, no timeout should ever be needed.
237                          * Otherwise, it's a mapperd or xseg bug.
238                          */
239                         xseg_wait_signal(xseg, 1000000UL);
240                 }
241         }
242         
243         /* Shouldn't reach this point */
244         always_assert(0);
245         return 0;
246 }
247
248 /*
249  * FIXME: Initialize the mapperd struct based on cmdline_* vars
250  */
251 static int mapperd_init(struct mapperd *mapperd)
252 {
253         int ret;
254
255         mapperd->mportno = cmdline_mportno;
256         mapperd->bportno = cmdline_bportno;
257
258         /* FIXME: If xseg library fails, is errno set? */
259         if (xseg_initialize()) {
260                 perr(PE, 0, "could not initialize xseg library");
261                 ret = -EIO;
262                 goto out;
263         }
264
265         if (! (mapperd->xseg = join_or_create(cmdline_xseg_spec))) {
266                 perr(PE, 0, "could not join or create xseg with spec '%s'\n",
267                         cmdline_xseg_spec);
268                 ret = -EIO;
269                 goto out_with_xseginit;
270         }
271
272         if (! (mapperd->mport = xseg_bind_port(mapperd->xseg, mapperd->mportno, NULL))) {
273                 perr(PE, 0, "cannot bind to xseg port %ld", (long)mapperd->mportno);
274                 ret = -EIO;
275                 goto out_with_xsegjoin;
276         }
277
278         mapperd->mportno = xseg_portno(mapperd->xseg, mapperd->mport);
279         xseg_init_local_signal(mapperd->xseg, mapperd->mportno);
280         perr(PI, 0, "mapperd on port %u of %u",
281                 mapperd->mportno, mapperd->xseg->config.nr_ports);
282
283         ret = 0;
284         goto out;
285
286 out_with_xsegjoin:
287         xseg_leave(mapperd->xseg);
288 out_with_xseginit:
289         xseg_finalize();
290 out:
291         return ret;
292 }
293
294 int main(int argc, char *argv[])
295 {
296         struct mapperd mapper;
297
298         init_perr("mapperd");
299         parse_cmdline(argc, argv);
300
301         perr(PI, 0, "v = %ld, m = %ld, b = %ld, nr_ops = %lu\n",
302                 cmdline_mportno, cmdline_mportno, cmdline_bportno, cmdline_nr_ops);
303
304         if (mapperd_init(&mapper) < 0) {
305                 perr(PFE, 0, "failed to initialize mapperd.");
306                 exit(1); /* This is just to quiesce gcc's control flow analysis */
307         }
308
309         return mapperd_loop(&mapper);
310 }
311