]> git.proxmox.com Git - mirror_corosync-qdevice.git/blob - qdevices/corosync-qdevice-tool.c
init: Fix init scripts to work with containers
[mirror_corosync-qdevice.git] / qdevices / corosync-qdevice-tool.c
1 /*
2 * Copyright (c) 2015-2016 Red Hat, Inc.
3 *
4 * All rights reserved.
5 *
6 * Author: Jan Friesse (jfriesse@redhat.com)
7 *
8 * This software licensed under BSD license, the text of which follows:
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 *
13 * - Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 * - Neither the name of the Red Hat, Inc. nor the names of its
19 * contributors may be used to endorse or promote products derived from this
20 * software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32 * THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #include <err.h>
36 #include <errno.h>
37 #include <getopt.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41
42 #include "qdevice-config.h"
43
44 #include "dynar.h"
45 #include "dynar-str.h"
46 #include "utils.h"
47 #include "unix-socket.h"
48
49 #define IPC_READ_BUF_SIZE 512
50
51 enum qdevice_tool_operation {
52 QDEVICE_TOOL_OPERATION_NONE,
53 QDEVICE_TOOL_OPERATION_SHUTDOWN,
54 QDEVICE_TOOL_OPERATION_STATUS,
55 };
56
57 enum qdevice_tool_exit_code {
58 QDEVICE_TOOL_EXIT_CODE_NO_ERROR = 0,
59 QDEVICE_TOOL_EXIT_CODE_USAGE = 1,
60 QDEVICE_TOOL_EXIT_CODE_INTERNAL_ERROR = 2,
61 QDEVICE_TOOL_EXIT_CODE_SOCKET_CONNECT = 3,
62 QDEVICE_TOOL_EXIT_CODE_QDEVICE_RETURNED_ERROR = 4,
63 };
64
65 static void
66 usage(void)
67 {
68
69 printf("usage: %s [-Hhsv] [-p qdevice_ipc_socket_path]\n",
70 QDEVICE_TOOL_PROGRAM_NAME);
71 }
72
73 static void
74 cli_parse(int argc, char * const argv[], enum qdevice_tool_operation *operation,
75 int *verbose, char **socket_path)
76 {
77 int ch;
78
79 *operation = QDEVICE_TOOL_OPERATION_NONE;
80 *verbose = 0;
81 *socket_path = strdup(QDEVICE_DEFAULT_LOCAL_SOCKET_FILE);
82
83 if (*socket_path == NULL) {
84 errx(QDEVICE_TOOL_EXIT_CODE_INTERNAL_ERROR,
85 "Can't alloc memory for socket path string");
86 }
87
88 while ((ch = getopt(argc, argv, "Hhsvp:")) != -1) {
89 switch (ch) {
90 case 'H':
91 *operation = QDEVICE_TOOL_OPERATION_SHUTDOWN;
92 break;
93 case 's':
94 *operation = QDEVICE_TOOL_OPERATION_STATUS;
95 break;
96 case 'v':
97 *verbose = 1;
98 break;
99 case 'p':
100 free(*socket_path);
101 *socket_path = strdup(optarg);
102 if (*socket_path == NULL) {
103 errx(QDEVICE_TOOL_EXIT_CODE_INTERNAL_ERROR,
104 "Can't alloc memory for socket path string");
105 }
106 break;
107 case 'h':
108 case '?':
109 usage();
110 exit(QDEVICE_TOOL_EXIT_CODE_USAGE);
111 break;
112 }
113 }
114
115 if (*operation == QDEVICE_TOOL_OPERATION_NONE) {
116 usage();
117 exit(QDEVICE_TOOL_EXIT_CODE_USAGE);
118 }
119 }
120
121 static int
122 store_command(struct dynar *str, enum qdevice_tool_operation operation, int verbose)
123 {
124 const char *nline = "\n\0";
125 const int nline_len = 2;
126
127 switch (operation) {
128 case QDEVICE_TOOL_OPERATION_NONE:
129 errx(QDEVICE_TOOL_EXIT_CODE_INTERNAL_ERROR, "Unhandled operation none");
130 break;
131 case QDEVICE_TOOL_OPERATION_SHUTDOWN:
132 if (dynar_str_cat(str, "shutdown ") != 0) {
133 return (-1);
134 }
135 break;
136 case QDEVICE_TOOL_OPERATION_STATUS:
137 if (dynar_str_cat(str, "status ") != 0) {
138 return (-1);
139 }
140 break;
141 }
142
143 if (verbose) {
144 if (dynar_str_cat(str, "verbose ") != 0) {
145 return (-1);
146 }
147 }
148
149 if (dynar_cat(str, nline, nline_len) != 0) {
150 return (-1);
151 }
152
153 return (0);
154 }
155
156 /*
157 * -1 - Internal error (can't alloc memory)
158 * 0 - No error
159 * 1 - IPC returned error
160 * 2 - Unknown status line
161 */
162 static int
163 read_ipc_reply(FILE *f)
164 {
165 struct dynar read_str;
166 int ch;
167 int status_readed;
168 int res;
169 static const char *ok_str = "OK";
170 static const char *err_str = "Error";
171 int err_set;
172 char c;
173
174 dynar_init(&read_str, IPC_READ_BUF_SIZE);
175
176 status_readed = 0;
177 err_set = 0;
178 res = 0;
179
180 while ((ch = fgetc(f)) != EOF) {
181 if (status_readed) {
182 putc(ch, (err_set ? stderr : stdout));
183 } else {
184 if (ch == '\r') {
185 } else if (ch == '\n') {
186 status_readed = 1;
187
188 c = '\0';
189 if (dynar_cat(&read_str, &c, sizeof(c)) != 0) {
190 res = -1;
191 goto exit_destroy;
192 }
193
194 if (strcasecmp(dynar_data(&read_str), ok_str) == 0) {
195 } else if (strcasecmp(dynar_data(&read_str), err_str) == 0) {
196 err_set = 1;
197 res = 1;
198 fprintf(stderr, "Error: ");
199 } else {
200 res = 2;
201 goto exit_destroy;
202 }
203 } else {
204 c = ch;
205 if (dynar_cat(&read_str, &c, sizeof(c)) != 0) {
206 res = -1;
207 goto exit_destroy;
208 }
209 }
210 }
211 }
212
213 exit_destroy:
214 dynar_destroy(&read_str);
215
216 return (res);
217 }
218
219 int
220 main(int argc, char * const argv[])
221 {
222 enum qdevice_tool_operation operation;
223 int verbose;
224 char *socket_path;
225 int sock_fd;
226 FILE *sock;
227 struct dynar send_str;
228 int res;
229 int exit_code;
230
231 exit_code = QDEVICE_TOOL_EXIT_CODE_NO_ERROR;
232
233 cli_parse(argc, argv, &operation, &verbose, &socket_path);
234
235 dynar_init(&send_str, QDEVICE_DEFAULT_IPC_MAX_RECEIVE_SIZE);
236
237 sock_fd = unix_socket_client_create(socket_path, 0);
238 if (sock_fd == -1) {
239 err(QDEVICE_TOOL_EXIT_CODE_SOCKET_CONNECT,
240 "Can't connect to QDevice socket (is QDevice running?)");
241 }
242
243 sock = fdopen(sock_fd, "w+t");
244 if (sock == NULL) {
245 err(QDEVICE_TOOL_EXIT_CODE_INTERNAL_ERROR, "Can't open QDevice socket fd");
246 }
247
248 if (store_command(&send_str, operation, verbose) != 0) {
249 errx(QDEVICE_TOOL_EXIT_CODE_INTERNAL_ERROR, "Can't store command");
250 }
251
252 res = fprintf(sock, "%s", dynar_data(&send_str));
253 if (res < 0 || (size_t)res != strlen(dynar_data(&send_str)) ||
254 fflush(sock) != 0) {
255 errx(QDEVICE_TOOL_EXIT_CODE_INTERNAL_ERROR, "Can't send command");
256 }
257
258 res = read_ipc_reply(sock);
259 switch (res) {
260 case -1:
261 errx(QDEVICE_TOOL_EXIT_CODE_INTERNAL_ERROR, "Internal error during IPC status line read");
262 break;
263 case 0:
264 break;
265 case 1:
266 exit_code = QDEVICE_TOOL_EXIT_CODE_QDEVICE_RETURNED_ERROR;
267 break;
268 case 2:
269 errx(QDEVICE_TOOL_EXIT_CODE_SOCKET_CONNECT, "Unknown status line returned by IPC server");
270 break;
271 }
272
273 if (fclose(sock) != 0) {
274 warn("Can't close QDevice socket");
275 }
276
277 free(socket_path);
278 dynar_destroy(&send_str);
279
280 return (exit_code);
281 }