1 /*	$Id: arms_pull.c 20800 2012-01-19 05:13:45Z m-oki $	*/
2 
3 /*
4  * Copyright (c) 2012, Internet Initiative Japan, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 /*
31  * API: arms_pull()
32  */
33 
34 #include "config.h"
35 
36 #include <inttypes.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <time.h>
40 #include <sys/queue.h>
41 
42 #include <openssl/ssl.h>
43 
44 #include <libarms.h>
45 #include <libarms_resource.h>
46 #include <armsd_conf.h> /* for ACMI */
47 #include <libarms_log.h>
48 
49 #include <libarms/time.h>
50 #include <libarms/ssl.h>
51 #include <scheduler/scheduler.h>
52 #include <transaction/transaction.h>
53 #include <protocol/arms_methods.h>
54 
55 
56 #define DEBUG
57 
58 #ifdef DEBUG
59 #define DPRINTF(...) libarms_log(ARMS_LOG_DEBUG, __VA_ARGS__)
60 #else
61 #define DPRINTF(...)
62 #endif
63 
64 static int
pull_timeout(struct arms_schedule * obj,int event)65 pull_timeout(struct arms_schedule *obj, int event)
66 {
67 	arms_context_t *res = arms_get_context();
68 
69 	switch (event) {
70 	case EVENT_TYPE_TIMEOUT:
71 		/* total timeout.  fatal.  stop to pull. */
72 		res->result = ARMS_ETIMEOUT;
73 		return SCHED_FINISHED_SCHEDULER;
74 	case EVENT_TYPE_FINISH:
75 		return SCHED_FINISHED_THIS;
76 	default:
77 		/*bug?*/
78 		return SCHED_CONTINUE_THIS;
79 	}
80 }
81 
82 int
arms_ls_pull(arms_context_t * res,const char * distid,struct timeval * timo)83 arms_ls_pull(arms_context_t *res, const char *distid, struct timeval *timo)
84 {
85 	int retry, ls_retry_max;
86 	int line, line_max;
87 	int sec, err;
88 
89 	ls_retry_max = acmi_retry_max(res->acmi, ACMI_CONFIG_RSSOL) * 500;
90 	line_max = acmi_get_max_line(res->acmi, ACMI_CONFIG_RSSOL);
91 	sec = acmi_retry_interval(res->acmi, ACMI_CONFIG_RSSOL) * 20;
92 	err = 0;
93 
94 	if (line_max == 0) {
95 		DPRINTF("line configuration not found.  see initial-config");
96 		res->trigger = "LS not found";
97 		arms_sleep(60);
98 		err = ARMS_EPULL;
99 	}
100 	DPRINTF("calculated. ls max retry %d times.", ls_retry_max);
101 	DPRINTF("calculated. ls retry interval %d sec.", sec);
102 	for (retry = 0; retry <= ls_retry_max; retry++) {
103 		if (retry > 0) {
104 			struct timeval now;
105 
106 			arms_monotime(&now);
107 			if (timercmp(&now, timo, >)) {
108 				/* global timeout. */
109 				res->result = ARMS_ETIMEOUT;
110 				DPRINTF("global timeout. ARMS_ETIMEOUT");
111 				break;
112 			}
113 			libarms_log(ARMS_LOG_ILS_ACCESS_RETRY,
114 			    "LS retry(%d/%d), wait %d sec.",
115 				    retry, ls_retry_max, sec);
116 			arms_sleep(sec);
117 		}
118 		for (line = 0; line < line_max; line++) {
119 			int derr;
120 
121 			err = arms_line_connect(
122 				res, ACMI_CONFIG_RSSOL, line, timo);
123 			if (err == ARMS_ECALLBACK) {
124 				/*fatal*/
125 				return ARMS_ECALLBACK;
126 			}
127 			if (err == 0) {
128 				/* setting pull schedule, and */
129 				if (new_ls_pull_transaction(res, distid) == 0) {
130 					/* go! */
131 					new_arms_schedule(SCHED_TYPE_TIMER, -1,
132 							  timo, pull_timeout,
133 							  NULL);
134 					res->result = ARMS_EMAXRETRY;
135 					arms_scheduler();
136 					err = res->result;
137 				} else {
138 					err = ARMS_ESYSTEM;
139 				}
140 			}
141 			if ((derr = arms_line_disconnect(
142 				     res, ACMI_CONFIG_RSSOL,
143 				     line, timo)) != 0) {
144 				/* ECALLBACK, or ETIMEOUT */
145 				if (derr == ARMS_ECALLBACK) {
146 					/*fatal*/
147 					return ARMS_ECALLBACK;
148 				}
149 				if (err == 0)
150 					err = res->result;
151 				break;
152 			}
153 			/* arms_scheduler result check */
154 			if (err == 0 ||
155 			    err == ARMS_EDONTRETRY ||
156 			    err == ARMS_EPULL ||
157 			    err == ARMS_EREBOOT)
158 				break;
159 		}
160 		if (err == 0 ||
161 		    err == ARMS_EDONTRETRY ||
162 		    err == ARMS_EPULL ||
163 		    err == ARMS_ECALLBACK ||
164 		    err == ARMS_EREBOOT)
165 			break;
166 	}
167 	return err;
168 }
169 
170 int
arms_rs_pull(arms_context_t * res,const char * distid,struct timeval * timo)171 arms_rs_pull(arms_context_t *res, const char *distid, struct timeval *timo)
172 {
173 	int retry, rs_retry_max;
174 	int line, line_max;
175 	int sec, err;
176 
177 	rs_retry_max = acmi_retry_max(res->acmi, ACMI_CONFIG_CONFSOL);
178 	line_max = acmi_get_max_line(res->acmi, ACMI_CONFIG_CONFSOL);
179 	sec = acmi_retry_interval(res->acmi, ACMI_CONFIG_CONFSOL);
180 	err = 0;
181 
182 	if (line_max == 0) {
183 		DPRINTF("line configuration not found.  see location-config");
184 		res->trigger = "RS not found";
185 		arms_sleep(60);
186 		err = ARMS_EPULL;
187 	}
188 	DPRINTF("calculated. rs max retry %d times.", rs_retry_max);
189 	DPRINTF("calculated. rs retry interval %d sec.", sec);
190 	for (retry = 0; retry <= rs_retry_max; retry++) {
191 		if (retry > 0) {
192 			struct timeval now;
193 
194 			arms_monotime(&now);
195 			if (timercmp(&now, timo, >)) {
196 				/* global timeout. */
197 				res->result = ARMS_ETIMEOUT;
198 				DPRINTF("global timeout. ARMS_ETIMEOUT");
199 				break;
200 			}
201 			libarms_log(ARMS_LOG_IRS_ACCESS_RETRY,
202 			    "RS retry(%d/%d), wait %d sec.",
203 				    retry, rs_retry_max, sec);
204 			arms_sleep(sec);
205 		}
206 		for (line = 0; line < line_max; line++) {
207 			int realline;
208 			int derr;
209 
210 			realline = (res->last_line + line) % line_max;
211 			err = arms_line_connect(
212 				res, ACMI_CONFIG_CONFSOL, realline, timo);
213 			if (err == ARMS_ECALLBACK) {
214 				/*fatal*/
215 				return ARMS_ECALLBACK;
216 			}
217 			if (err == 0) {
218 				/* setting pull schedule, and */
219 				if (new_rs_pull_transaction(res, distid) == 0) {
220 					/* go! */
221 					new_arms_schedule(SCHED_TYPE_TIMER, -1,
222 							  timo, pull_timeout,
223 							  NULL);
224 					res->result = ARMS_EMAXRETRY;
225 					arms_scheduler();
226 					err = res->result;
227 					if (err == 0) {
228 						res->last_line = realline;
229 					}
230 				} else {
231 					err = ARMS_ESYSTEM;
232 				}
233 			}
234 			if ((derr = arms_line_disconnect(
235 				     res, ACMI_CONFIG_CONFSOL,
236 				     realline, timo)) != 0) {
237 				/* ECALLBACK, or ETIMEOUT */
238 				if (derr == ARMS_ECALLBACK) {
239 					/*fatal*/
240 					return ARMS_ECALLBACK;
241 				}
242 				if (err == 0)
243 					err = res->result;
244 				break;
245 			}
246 			/* arms_scheduler result check */
247 			if (err == 0 ||
248 			    err == ARMS_EDONTRETRY ||
249 			    err == ARMS_EPULL ||
250 			    err == ARMS_EREBOOT)
251 				break;
252 		}
253 		if (err == 0 ||
254 		    err == ARMS_EDONTRETRY ||
255 		    err == ARMS_EPULL ||
256 		    err == ARMS_ECALLBACK ||
257 		    err == ARMS_EREBOOT)
258 			break;
259 	}
260 	return err;
261 }
262 
263 int
arms_pull(arms_context_t * res,time_t timeout,size_t fragment,arms_callback_tbl_t * cb_tbl,arms_line_desc_t * lines,void * udata)264 arms_pull(arms_context_t *res,
265 	  time_t timeout, size_t fragment, arms_callback_tbl_t *cb_tbl,
266 	  arms_line_desc_t *lines, void *udata)
267 {
268 #ifdef HAVE_SIGNAL
269 	struct sigaction oldact, newact;
270 #endif
271 	struct timeval timo;
272 	char *distid;
273 
274 	/* check parameter */
275 	if (timeout < ARMS_MIN_TIMEOUT && timeout != 0)
276 		return ARMS_EINVAL;
277 
278 	if (timeout > ARMS_MAX_TIMEOUT)
279 		return ARMS_EINVAL;
280 
281 	if (timeout != 0)
282 		res->timeout = timeout;
283 	else
284 		res->timeout = ARMS_DEFAULT_TIMEOUT;
285 
286 	if (cb_tbl == NULL || lines == NULL) {
287 		return ARMS_EINVAL;
288 	}
289 	/* setup */
290 	acmi_set_lines(res->acmi, ACMI_CONFIG_RSSOL, lines);
291 	acmi_reset_line(res->acmi, ACMI_CONFIG_RSSOL);
292 
293 	arms_free_hbtinfo(res);
294 	arms_free_rsinfo(res);
295 	arms_free_rs_tunnel_url(res);
296 	memset(res->hbt_info, 0, sizeof(res->hbt_info));
297 	memset(res->rs_push_address, 0, sizeof(res->rs_push_address));
298 	memset(res->rs_pull_url, 0, sizeof(res->rs_pull_url));
299 	res->fragment = fragment;
300 	res->line_af = AF_UNSPEC;
301 	memcpy(&res->callbacks, cb_tbl, sizeof(res->callbacks));
302 	res->udata = udata;
303 	if (res->trigger == NULL)
304 		res->trigger = "power on boot";
305 	arms_scheduler_init();
306 
307 #ifdef HAVE_SIGNAL
308 	/* block SIGPIPE */
309 	memset(&newact, 0, sizeof(newact));
310 	memset(&oldact, 0, sizeof(oldact));
311 	newact.sa_handler = SIG_IGN;
312 	sigaction(SIGPIPE, &newact, &oldact);
313 #endif
314 	/* setting timer of total timeout */
315 	arms_get_time_remaining(&timo, res->timeout);
316 	new_arms_schedule(SCHED_TYPE_TIMER, -1, &timo, pull_timeout, NULL);
317 
318 	/* HTTP/1.0 */
319 	res->http_preferred_version = 0;
320 
321 	distid = strdistid(&res->dist_id);
322 
323 	for (;;) {
324 		if (res->rs_endpoint[0] == '\0') {
325 			/* reset last RS line */
326 			res->last_line = 0;
327 
328 			libarms_log(ARMS_LOG_ILS_ACCESS_START,
329 			    "Pull from LS.");
330 			arms_set_global_state(ARMS_ST_LSPULL);
331 			acmi_clear(res->acmi, ACMI_CONFIG_CONFSOL);
332 			res->result = arms_ls_pull(res, distid, &timo);
333 			if (res->result != 0) {
334 				/* fatal. */
335 				break;
336 			}
337 		} else {
338 			libarms_log(ARMS_LOG_IRS_ACCESS_START,
339 			    "Skip LS access.  Pull from RS.");
340 		}
341 #if 0
342 		acmi_dump(res->acmi);
343 #endif
344 
345 		arms_set_global_state(ARMS_ST_RSPULL);
346 		res->result = arms_rs_pull(res, distid, &timo);
347 		if (res->result == 0 ||
348 		    res->result == ARMS_ETIMEOUT ||
349 		    res->result == ARMS_EDONTRETRY ||
350 		    res->result == ARMS_ECALLBACK ||
351 		    res->result == ARMS_EREBOOT) {
352 			/* RS pull suucess, fatal error, or total timeout */
353 			break;
354 		}
355 		/* other result (includes ARMS_EPULL): RS -> LS fallback. */
356 		res->rs_endpoint[0] = '\0';
357 	}
358 
359 #ifdef HAVE_SIGNAL
360 	/* restore SIGPIPE */
361 	sigaction(SIGPIPE, &oldact, NULL);
362 #endif
363 
364 	if (res->result == 0) {
365 		/* update heartbeat information */
366 		arms_hb_update_server(&res->hb_ctx,
367 				      res->hbt_info, res->num_of_hbt);
368 
369 		arms_set_global_state(ARMS_ST_PULLDONE);
370 	} else {
371 		arms_set_global_state(ARMS_ST_BOOT_FAIL);
372 	}
373 	return res->result;
374 }
375