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