examples: add getopt argument parsing for client and server
[libcmdline.git] / src / event_server / main.c
1 /*
2  * Copyright (c) 2011, Olivier MATZ <zer0@droids-corp.org>
3  * All rights reserved.
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of the University of California, Berkeley nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdint.h>
32 #include <stdlib.h>
33 #include <stdarg.h>
34 #include <inttypes.h>
35 #include <ctype.h>
36 #include <unistd.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39
40 #include <getopt.h>
41
42 #include <cmdline_rdline.h>
43 #include <cmdline_parse.h>
44 #include <cmdline_socket.h>
45 #include <cmdline.h>
46
47 #include <event.h>
48
49 static struct sockaddr addr;
50 static uint16_t port = 1234;
51
52 extern cmdline_parse_ctx_t main_ctx[];
53
54 static void
55 evt_server_input(int s, short event, void *arg)
56 {
57         struct cmdline *cl = arg;
58         struct event *evt = cl->opaque;
59         char buf[2048];
60         int n;
61
62         n = read(s, buf, sizeof(buf));
63         if (n < 0) {
64                 event_del(evt);
65                 free(evt);
66                 cmdline_free(cl);
67                 return;
68         }
69
70         if (cmdline_in(cl, buf, n) < 0) {
71                 event_del(evt);
72                 free(evt);
73                 cmdline_free(cl);
74                 return;
75         }
76 }
77
78 static void
79 evt_server_new_connection(int s, short event, void *arg)
80 {
81         struct cmdline *cl;
82         struct event *evt;
83
84         /* create a new cmdline instance for this connection */
85         cl = cmdline_accept(main_ctx, "example> ", s);
86
87         /* allocate a new event for this incomming connection */
88         evt = malloc(sizeof(struct event));
89         if (evt == NULL) {
90                 cmdline_free(cl);
91                 return;
92         }
93
94         cl->opaque = evt;
95         event_set(evt, cl->s_in, EV_READ | EV_PERSIST, evt_server_input, cl);
96         event_add(evt, NULL);
97 }
98
99 static void
100 usage(const char *prgname)
101 {
102         printf("%s [-a ADDRESS] [-p PORT]\n"
103                "\n"
104                "Start a command line server on a TCP listen socket. This example\n"
105                "uses the libevent to support several simultaneous connections.\n"
106                "The default behaviour is to listen on 0.0.0.0:1234\n"
107                "\n"
108                "  -a ADDR:  IPv4 or IPv6 local address to listen on (default\n"
109                "            is 0.0.0.0).\n"
110                "  -p PORT:  port to listen on (default is 1234).\n",
111                prgname);
112 }
113
114 /* Parse the argument given in the command line of the application */
115 static int
116 parse_args(int argc, char **argv)
117 {
118         int opt, ret;
119         char **argvopt;
120         int option_index;
121         char *prgname = argv[0];
122         char *end = NULL;
123         static struct option lgopts[] = {
124                 {0, 0, 0, 0}
125         };
126
127         argvopt = argv;
128
129         while ((opt = getopt_long(argc, argvopt, "a:p:",
130                                   lgopts, &option_index)) != EOF) {
131
132                 switch (opt) {
133
134                 case 'a':
135                         if (strchr(optarg, '.') != NULL) {
136                                 struct sockaddr_in *sin =
137                                         (struct sockaddr_in *)&addr;
138                                 addr.sa_family = AF_INET;
139                                 ret = inet_pton(AF_INET, optarg,
140                                                 &sin->sin_addr);
141                         }
142                         else {
143                                 struct sockaddr_in6 *sin6 =
144                                         (struct sockaddr_in6 *)&addr;
145                                 addr.sa_family = AF_INET6;
146                                 ret = inet_pton(AF_INET6, optarg,
147                                                 &sin6->sin6_addr);
148                         }
149
150                         if (ret != 1) {
151                                 printf("Bad address\n");
152                                 usage(prgname);
153                                 return -1;
154                         }
155                         break;
156
157                 case 'p':
158                         port = strtoul(optarg, &end, 0);
159                         if ((optarg[0] == '\0') ||
160                             (end == NULL) ||
161                             (*end != '\0')) {
162                                 printf("Invalid port\n");
163                                 usage(prgname);
164                                 return -1;
165                         }
166
167                         break;
168
169                 /* long options */
170                 case 0:
171                         /* if (!strcmp(lgopts[option_index].name, "option")) */
172                         break;
173
174                 default:
175                         usage(prgname);
176                         return -1;
177                 }
178         }
179
180         if (argc != optind) {
181                 printf("Invalid argument\n");
182                 usage(prgname);
183                 return -1;
184         }
185
186         ret = optind-1;
187
188         return ret;
189 }
190
191 int main(int argc, char **argv)
192 {
193         struct sockaddr_in *sin = (struct sockaddr_in *)&addr;
194         struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr;
195         struct event listen_evt;
196         int s;
197
198         if (parse_args(argc, argv) < 0)
199                 exit(1);
200
201         printf("Listening on port %d\n", port);
202
203         /* initializa libevent */
204         event_init();
205
206         /* open a tcp server on specified port */
207         if (addr.sa_family == AF_INET)
208                 s = cmdline_tcpv4_listen(sin->sin_addr.s_addr, port);
209         else
210                 s = cmdline_tcpv6_listen(sin6->sin6_addr, port);
211         event_set(&listen_evt, s, EV_READ | EV_PERSIST,
212                   evt_server_new_connection, NULL);
213         event_add(&listen_evt, NULL);
214
215         /* libevent main loop */
216         event_dispatch();
217
218         return 0;
219 }