Fix the strange bundles' members looping use.
[l2tpns.git] / nsctl.c
1 /* l2tpns plugin control */
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6 #include <errno.h>
7 #include <string.h>
8 #include <netdb.h>
9 #include <signal.h>
10
11 #include "l2tpns.h"
12 #include "control.h"
13
14 struct {
15 char *command;
16 char *usage;
17 int action;
18 } builtins[] = {
19 { "load_plugin", " PLUGIN Load named plugin", NSCTL_REQ_LOAD },
20 { "unload_plugin", " PLUGIN Unload named plugin", NSCTL_REQ_UNLOAD },
21 { "help", " List available commands", NSCTL_REQ_HELP },
22 { 0 }
23 };
24
25 static int debug = 0;
26 static int timeout = 2; // 2 seconds
27 static char *me;
28
29 #define USAGE() fprintf(stderr, "Usage: %s [-d] [-h HOST[:PORT]] [-t TIMEOUT] COMMAND [ARG ...]\n", me)
30
31 static struct nsctl *request(char *host, int port, int type, int argc, char *argv[]);
32
33 int main(int argc, char *argv[])
34 {
35 int req_type = 0;
36 char *host = 0;
37 int port;
38 int i;
39 char *p;
40 struct nsctl *res;
41
42 if ((p = strrchr((me = argv[0]), '/')))
43 me = p + 1;
44
45 opterr = 0;
46 while ((i = getopt(argc, argv, "dh:t:")) != -1)
47 switch (i)
48 {
49 case 'd':
50 debug++;
51 break;
52
53 case 'h':
54 host = optarg;
55 break;
56
57 case 't':
58 timeout = atoi(optarg);
59 break;
60
61 default:
62 USAGE();
63 return EXIT_FAILURE;
64 }
65
66 argc -= optind;
67 argv += optind;
68
69 if (argc < 1 || !argv[0][0])
70 {
71 USAGE();
72 return EXIT_FAILURE;
73 }
74
75 if (!host)
76 host = "127.0.0.1";
77
78 if ((p = strchr(host, ':')))
79 {
80 port = atoi(p + 1);
81 if (!port)
82 {
83 fprintf(stderr, "%s: invalid port `%s'\n", me, p + 1);
84 return EXIT_FAILURE;
85 }
86
87 *p = 0;
88 }
89 else
90 {
91 port = NSCTL_PORT;
92 }
93
94 for (i = 0; !req_type && builtins[i].command; i++)
95 if (!strcmp(argv[0], builtins[i].command))
96 req_type = builtins[i].action;
97
98 if (req_type == NSCTL_REQ_HELP)
99 {
100 printf("Available commands:\n");
101 for (i = 0; builtins[i].command; i++)
102 printf(" %s%s\n", builtins[i].command, builtins[i].usage);
103 }
104
105 if (req_type)
106 {
107 argc--;
108 argv++;
109 }
110 else
111 {
112 req_type = NSCTL_REQ_CONTROL;
113 }
114
115 if ((res = request(host, port, req_type, argc, argv)))
116 {
117 FILE *stream = stderr;
118 int status = EXIT_FAILURE;
119
120 if (res->type == NSCTL_RES_OK)
121 {
122 stream = stdout;
123 status = EXIT_SUCCESS;
124 }
125
126 for (i = 0; i < res->argc; i++)
127 fprintf(stream, "%s\n", res->argv[i]);
128
129 return status;
130 }
131
132 return EXIT_FAILURE;
133 }
134
135 static void sigalrm_handler(int sig) { }
136
137 static struct nsctl *request(char *host, int port, int type, int argc, char *argv[])
138 {
139 static struct nsctl res;
140 struct sockaddr_in peer;
141 socklen_t len = sizeof(peer);
142 struct hostent *h = gethostbyname(host);
143 int fd;
144 uint8_t buf[NSCTL_MAX_PKT_SZ];
145 int sz;
146 char *err;
147
148 if (!h || h->h_addrtype != AF_INET)
149 {
150 fprintf(stderr, "%s: invalid host `%s'\n", me, host);
151 return 0;
152 }
153
154 if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
155 {
156 fprintf(stderr, "%s: can't create udp socket (%s)\n", me, strerror(errno));
157 return 0;
158 }
159
160 memset(&peer, 0, len);
161 peer.sin_family = AF_INET;
162 peer.sin_port = htons(port);
163 memcpy(&peer.sin_addr.s_addr, h->h_addr, sizeof(peer.sin_addr.s_addr));
164
165 if (connect(fd, (struct sockaddr *) &peer, sizeof(peer)) < 0)
166 {
167 fprintf(stderr, "%s: udp connect failed (%s)\n", me, strerror(errno));
168 return 0;
169 }
170
171 if ((sz = pack_control(buf, sizeof(buf), type, argc, argv)) < 0)
172 {
173 fprintf(stderr, "%s: error packing request\n", me);
174 return 0;
175 }
176
177 if (debug)
178 {
179 struct nsctl req;
180 if (unpack_control(&req, buf, sz) == type)
181 {
182 fprintf(stderr, "Sending ");
183 dump_control(&req, stderr);
184 }
185 }
186
187 if (send(fd, buf, sz, 0) < 0)
188 {
189 fprintf(stderr, "%s: error sending request (%s)\n", me, strerror(errno));
190 return 0;
191 }
192
193 /* set timer */
194 if (timeout)
195 {
196 struct sigaction alrm;
197 alrm.sa_handler = sigalrm_handler;
198 sigemptyset(&alrm.sa_mask);
199 alrm.sa_flags = 0;
200
201 sigaction(SIGALRM, &alrm, 0);
202 alarm(timeout);
203 }
204
205 sz = recv(fd, buf, sizeof(buf), 0);
206 alarm(0);
207
208 if (sz < 0)
209 {
210 fprintf(stderr, "%s: error receiving response (%s)\n", me,
211 errno == EINTR ? "timed out" : strerror(errno));
212
213 return 0;
214 }
215
216 if ((type = unpack_control(&res, buf, sz)) > 0 && type & NSCTL_RESPONSE)
217 {
218 if (debug)
219 {
220 fprintf(stderr, "Received ");
221 dump_control(&res, stderr);
222 }
223
224 return &res;
225 }
226
227 err = "unknown error";
228 switch (type)
229 {
230 case NSCTL_ERR_SHORT: err = "short packet"; break;
231 case NSCTL_ERR_LONG: err = "extra data"; break;
232 case NSCTL_ERR_MAGIC: err = "bad magic"; break;
233 case NSCTL_ERR_TYPE: err = "invalid type"; break;
234 }
235
236 fprintf(stderr, "%s: %s\n", me, err);
237 return 0;
238 }