1 /*****************************************************************************\
2 * $Id: ipmiconsole_engine.c,v 1.97 2010-02-08 22:02:30 chu11 Exp $
3 *****************************************************************************
4 * Copyright (C) 2007-2015 Lawrence Livermore National Security, LLC.
5 * Copyright (C) 2006-2007 The Regents of the University of California.
6 * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
7 * Written by Albert Chu <chu11@llnl.gov>
8 * UCRL-CODE-221226
9 *
10 * This file is part of Ipmiconsole, a set of IPMI 2.0 SOL libraries
11 * and utilities. For details, see http://www.llnl.gov/linux/.
12 *
13 * Ipmiconsole is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by the
15 * Free Software Foundation; either version 3 of the License, or (at your
16 * option) any later version.
17 *
18 * Ipmiconsole is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 * for more details.
22 *
23 * You should have received a copy of the GNU General Public License along
24 * with Ipmiconsole. If not, see <http://www.gnu.org/licenses/>.
25 \*****************************************************************************/
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif /* HAVE_CONFIG_H */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #if STDC_HEADERS
34 #include <string.h>
35 #endif /* STDC_HEADERS */
36 #if HAVE_PTHREAD_H
37 #include <pthread.h>
38 #endif /* HAVE_PTHREAD_H */
39 #if HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif /* HAVE_UNISTD_H */
42 #include <sys/types.h>
43 #include <sys/poll.h>
44 #include <signal.h>
45 #include <limits.h>
46 #include <assert.h>
47 #include <errno.h>
48 #include <freeipmi/freeipmi.h>
49
50 #include "ipmiconsole.h"
51 #include "ipmiconsole_defs.h"
52
53 #include "ipmiconsole_ctx.h"
54 #include "ipmiconsole_engine.h"
55 #include "ipmiconsole_debug.h"
56 #include "ipmiconsole_garbage_collector.h"
57 #include "ipmiconsole_processing.h"
58 #include "ipmiconsole_util.h"
59 #include "scbuf.h"
60
61 #include "freeipmi-portability.h"
62 #include "list.h"
63 #include "secure.h"
64
65 /*
66 * Locking notes:
67 *
68 * when is_count mutex is locked - thread_count_mutex can be locked, not vice versa
69 * when is_count mutex is locked - teardown_mutex can be locked, not vice versa
70 * when thread_count mutex is locked - ctxs_mutex can be locked, not vice versa
71 */
72 static int console_engine_is_setup = 0;
73 static pthread_mutex_t console_engine_is_setup_mutex = PTHREAD_MUTEX_INITIALIZER;
74
75 static unsigned int console_engine_thread_count = 0;
76 static pthread_mutex_t console_engine_thread_count_mutex = PTHREAD_MUTEX_INITIALIZER;
77
78 static int console_engine_teardown = 0;
79 static int console_engine_teardown_immediate = 0;
80 static pthread_mutex_t console_engine_teardown_mutex = PTHREAD_MUTEX_INITIALIZER;
81
82 static List console_engine_ctxs[IPMICONSOLE_THREAD_COUNT_MAX];
83 static unsigned int console_engine_ctxs_count[IPMICONSOLE_THREAD_COUNT_MAX];
84 static pthread_mutex_t console_engine_ctxs_mutex[IPMICONSOLE_THREAD_COUNT_MAX];
85
86 /* In the core engine code, the poll() may sit for a large number of
87 * seconds, waiting for the next event to happen. In the meantime, a
88 * user may have submitted a new context or wants to close the engine.
89 * The poll() doesn't know this and will sit until it times out,
90 * letting the user sit and wait for the engine loop to "come around
91 * again" and start processing. This pipe can be used to "interrupt"
92 * the poll() when the user wants to get things moving a little
93 * faster.
94 */
95 static int console_engine_ctxs_notifier[IPMICONSOLE_THREAD_COUNT_MAX][2];
96 static unsigned int console_engine_ctxs_notifier_num = 0;
97
98 /*
99 * The engine is capable of "being finished" with a context before the
100 * user has called ipmiconsole_ctx_destroy(). So we need to stick the
101 * context somewhere and garbage collect the memory back later.
102 *
103 * The garbage collector notifier is similar to the engine ctxs
104 * notifier above, although the garbage collector notifier will only
105 * be used to tell it to exit, not anything else.
106 */
107 List console_engine_ctxs_to_destroy = NULL;
108 pthread_mutex_t console_engine_ctxs_to_destroy_mutex = PTHREAD_MUTEX_INITIALIZER;
109 int garbage_collector_notifier[2];
110
111 extern int garbage_collector_active;
112 extern pthread_mutex_t garbage_collector_active_mutex;
113 extern pthread_cond_t garbage_collector_active_cond;
114
115 /* See comments below in _poll_setup(). */
116 static int dummy_fd = -1;
117
118 struct _ipmiconsole_poll_data {
119 struct pollfd *pfds;
120 ipmiconsole_ctx_t *pfds_ctxs;
121 unsigned int ctxs_len;
122 unsigned int pfds_index;
123 };
124
125 #define IPMICONSOLE_SPIN_WAIT_TIME 250000
126
127 #define IPMICONSOLE_PIPE_BUFLEN 1024
128
129 static int
_ipmiconsole_garbage_collector_create(void)130 _ipmiconsole_garbage_collector_create (void)
131 {
132 pthread_t thread;
133 pthread_attr_t attr;
134 int perr, rv = -1;
135
136 assert (!console_engine_is_setup);
137
138 if ((perr = pthread_attr_init (&attr)))
139 {
140 IPMICONSOLE_DEBUG (("pthread_attr_init: %s", strerror (perr)));
141 errno = perr;
142 goto cleanup;
143 }
144
145 if ((perr = pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED)))
146 {
147 IPMICONSOLE_DEBUG (("pthread_attr_setdetachstate: %s", strerror (perr)));
148 errno = perr;
149 goto cleanup;
150 }
151
152 if ((perr = pthread_create (&thread, &attr, ipmiconsole_garbage_collector, NULL)))
153 {
154 IPMICONSOLE_DEBUG (("pthread_create: %s", strerror (perr)));
155 errno = perr;
156 goto cleanup;
157 }
158
159 /* Who cares if this fails */
160 if ((perr = pthread_attr_destroy (&attr)))
161 IPMICONSOLE_DEBUG (("pthread_attr_destroy: %s", strerror (perr)));
162
163 if ((perr = pthread_mutex_lock (&garbage_collector_active_mutex)))
164 {
165 IPMICONSOLE_DEBUG (("pthread_mutex_lock: %s", strerror (perr)));
166 errno = perr;
167 goto cleanup;
168 }
169
170 while (!garbage_collector_active)
171 {
172 if ((perr = pthread_cond_wait (&garbage_collector_active_cond,
173 &garbage_collector_active_mutex)))
174 {
175 IPMICONSOLE_DEBUG (("pthread_cond_wait: %s", strerror (perr)));
176 errno = perr;
177 goto cleanup;
178 }
179 }
180
181 if ((perr = pthread_mutex_unlock (&garbage_collector_active_mutex)))
182 {
183 IPMICONSOLE_DEBUG (("pthread_mutex_unlock: %s", strerror (perr)));
184 errno = perr;
185 goto cleanup;
186 }
187
188 rv = 0;
189 cleanup:
190 return (rv);
191 }
192
193 /* Notes: One of the reason we do not create the threads in this
194 * function, is that it would make it more difficult to properly
195 * "cleanup" on an error. We wouldn't know how many threads were
196 * created, flags for setup completion may not be set yet, etc.
197 *
198 * Therefore ipmiconsole_engine_thread_create() is done outside of
199 * this function and is done elsewhere.
200 */
201 int
ipmiconsole_engine_setup(unsigned int thread_count)202 ipmiconsole_engine_setup (unsigned int thread_count)
203 {
204 unsigned int i;
205 int perr;
206
207 assert (!console_engine_thread_count);
208 assert (thread_count && thread_count <= IPMICONSOLE_THREAD_COUNT_MAX);
209
210 if ((perr = pthread_mutex_lock (&console_engine_is_setup_mutex)))
211 {
212 IPMICONSOLE_DEBUG (("pthread_mutex_lock: %s", strerror (perr)));
213 errno = perr;
214 return (-1);
215 }
216
217 memset (console_engine_ctxs, '\0', IPMICONSOLE_THREAD_COUNT_MAX * sizeof (List));
218 memset (console_engine_ctxs_count, '\0', IPMICONSOLE_THREAD_COUNT_MAX * sizeof (unsigned int));
219 memset (console_engine_ctxs_mutex, '\0', IPMICONSOLE_THREAD_COUNT_MAX * sizeof (pthread_mutex_t));
220 for (i = 0; i < IPMICONSOLE_THREAD_COUNT_MAX; i++)
221 {
222 console_engine_ctxs_notifier[i][0] = -1;
223 console_engine_ctxs_notifier[i][1] = -1;
224 }
225 garbage_collector_notifier[0] = -1;
226 garbage_collector_notifier[1] = -1;
227
228 if (ipmi_rmcpplus_init () < 0)
229 {
230 if (errno == EPERM)
231 IPMICONSOLE_DEBUG (("ipmi_rmcpplus_init: incompatible crypto library"));
232 else
233 IPMICONSOLE_DEBUG (("ipmi_rmcpplus_init: %s", strerror (errno)));
234 goto cleanup;
235 }
236
237 for (i = 0; i < IPMICONSOLE_THREAD_COUNT_MAX; i++)
238 {
239 if (!(console_engine_ctxs[i] = list_create ((ListDelF)ipmiconsole_ctx_connection_cleanup_session_submitted)))
240 {
241 IPMICONSOLE_DEBUG (("list_create: %s", strerror (errno)));
242 goto cleanup;
243 }
244 console_engine_ctxs_count[i] = 0;
245 if ((perr = pthread_mutex_init (&console_engine_ctxs_mutex[i], NULL)) != 0)
246 {
247 IPMICONSOLE_DEBUG (("pthread_mutex_init: %s", strerror (perr)));
248 goto cleanup;
249 }
250 }
251
252 /* Don't create fds for all ctxs_notifier to limit fd creation */
253 console_engine_ctxs_notifier_num = thread_count;
254 for (i = 0; i < console_engine_ctxs_notifier_num; i++)
255 {
256 if (pipe (console_engine_ctxs_notifier[i]) < 0)
257 {
258 IPMICONSOLE_DEBUG (("pipe: %s", strerror (errno)));
259 goto cleanup;
260 }
261
262 if (ipmiconsole_set_closeonexec (NULL, console_engine_ctxs_notifier[i][0]) < 0)
263 {
264 IPMICONSOLE_DEBUG (("closeonexec error"));
265 goto cleanup;
266 }
267
268 if (ipmiconsole_set_closeonexec (NULL, console_engine_ctxs_notifier[i][1]) < 0)
269 {
270 IPMICONSOLE_DEBUG (("closeonexec error"));
271 goto cleanup;
272 }
273 }
274
275 if (pipe (garbage_collector_notifier) < 0)
276 {
277 IPMICONSOLE_DEBUG (("pipe: %s", strerror (errno)));
278 goto cleanup;
279 }
280
281 if (ipmiconsole_set_closeonexec (NULL, garbage_collector_notifier[0]) < 0)
282 {
283 IPMICONSOLE_DEBUG (("closeonexec error"));
284 goto cleanup;
285 }
286
287 if (ipmiconsole_set_closeonexec (NULL, garbage_collector_notifier[1]) < 0)
288 {
289 IPMICONSOLE_DEBUG (("closeonexec error"));
290 goto cleanup;
291 }
292
293 if (!(console_engine_ctxs_to_destroy = list_create ((ListDelF)ipmiconsole_ctx_garbage_collection_cleanup)))
294 {
295 IPMICONSOLE_DEBUG (("list_create: %s", strerror (errno)));
296 goto cleanup;
297 }
298
299 if ((dummy_fd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
300 {
301 IPMICONSOLE_DEBUG (("socket: %s", strerror (errno)));
302 goto cleanup;
303 }
304
305 if (_ipmiconsole_garbage_collector_create () < 0)
306 goto cleanup;
307
308 console_engine_is_setup++;
309 console_engine_teardown = 0;
310 console_engine_teardown_immediate = 0;
311
312 if ((perr = pthread_mutex_unlock (&console_engine_is_setup_mutex)))
313 {
314 IPMICONSOLE_DEBUG (("pthread_mutex_unlock: %s", strerror (perr)));
315 errno = perr;
316 goto cleanup;
317 }
318
319 return (0);
320
321 cleanup:
322 for (i = 0; i < IPMICONSOLE_THREAD_COUNT_MAX; i++)
323 {
324 if (console_engine_ctxs[i])
325 {
326 list_destroy (console_engine_ctxs[i]);
327 pthread_mutex_destroy (&console_engine_ctxs_mutex[i]);
328 }
329 console_engine_ctxs[i] = NULL;
330 /* ignore potential error, cleanup path */
331 close (console_engine_ctxs_notifier[i][0]);
332 /* ignore potential error, cleanup path */
333 close (console_engine_ctxs_notifier[i][1]);
334 }
335 if (console_engine_ctxs_to_destroy)
336 list_destroy (console_engine_ctxs_to_destroy);
337 console_engine_ctxs_to_destroy = NULL;
338 garbage_collector_notifier[0] = -1;
339 garbage_collector_notifier[1] = -1;
340 /* ignore potential error, cleanup path */
341 close (dummy_fd);
342 dummy_fd = -1;
343
344 if ((perr = pthread_mutex_unlock (&console_engine_is_setup_mutex)))
345 IPMICONSOLE_DEBUG (("pthread_mutex_unlock: %s", strerror (perr)));
346
347 return (-1);
348 }
349
350 int
ipmiconsole_engine_is_setup(void)351 ipmiconsole_engine_is_setup (void)
352 {
353 int is_setup, perr;
354
355 if ((perr = pthread_mutex_lock (&console_engine_is_setup_mutex)))
356 {
357 IPMICONSOLE_DEBUG (("pthread_mutex_lock: %s", strerror (perr)));
358 return (-1);
359 }
360
361 is_setup = console_engine_is_setup;
362
363 if ((perr = pthread_mutex_unlock (&console_engine_is_setup_mutex)))
364 {
365 IPMICONSOLE_DEBUG (("pthread_mutex_unlock: %s", strerror (perr)));
366 return (-1);
367 }
368
369 return (is_setup);
370 }
371
372 int
ipmiconsole_engine_thread_count(void)373 ipmiconsole_engine_thread_count (void)
374 {
375 int thread_count, perr;
376
377 if ((perr = pthread_mutex_lock (&console_engine_thread_count_mutex)))
378 {
379 IPMICONSOLE_DEBUG (("pthread_mutex_lock: %s", strerror (perr)));
380 return (-1);
381 }
382
383 if (console_engine_thread_count > INT_MAX)
384 thread_count = INT_MAX;
385 else
386 thread_count = console_engine_thread_count;
387
388 if ((perr = pthread_mutex_unlock (&console_engine_thread_count_mutex)))
389 {
390 IPMICONSOLE_DEBUG (("pthread_mutex_unlock: %s", strerror (perr)));
391 return (-1);
392 }
393
394 return (thread_count);
395 }
396
397 static int
_teardown_initiate(void * x,void * arg)398 _teardown_initiate (void *x, void *arg)
399 {
400 ipmiconsole_ctx_t c;
401
402 assert (x);
403
404 c = (ipmiconsole_ctx_t)x;
405
406 assert (c);
407 assert (c->magic == IPMICONSOLE_CTX_MAGIC);
408
409 if (!c->session.close_session_flag)
410 c->session.close_session_flag++;
411
412 return (0);
413 }
414
415 static int
_poll_setup(void * x,void * arg)416 _poll_setup (void *x, void *arg)
417 {
418 ipmiconsole_ctx_t c;
419 struct _ipmiconsole_poll_data *poll_data;
420
421 assert (x);
422 assert (arg);
423
424 c = (ipmiconsole_ctx_t)x;
425
426 assert (c);
427 assert (c->magic == IPMICONSOLE_CTX_MAGIC);
428
429 poll_data = (struct _ipmiconsole_poll_data *)arg;
430
431 poll_data->pfds[poll_data->pfds_index*3].fd = c->connection.ipmi_fd;
432 poll_data->pfds[poll_data->pfds_index*3].events = 0;
433 poll_data->pfds[poll_data->pfds_index*3].revents = 0;
434 poll_data->pfds[poll_data->pfds_index*3].events |= POLLIN;
435 if (!scbuf_is_empty (c->connection.ipmi_to_bmc))
436 poll_data->pfds[poll_data->pfds_index*3].events |= POLLOUT;
437
438 /* If the session is being torn down, don't bother settings flags on
439 * these fds. However, to avoid spinning due to an invalid fd or a
440 * closed fd (i.e. get a POLLINVAL or POLLHUP), use the dummy_fd.
441 */
442 if (!c->session.close_session_flag)
443 {
444 poll_data->pfds[poll_data->pfds_index*3 + 1].fd = c->connection.asynccomm[0];
445 poll_data->pfds[poll_data->pfds_index*3 + 1].events = 0;
446 poll_data->pfds[poll_data->pfds_index*3 + 1].revents = 0;
447 poll_data->pfds[poll_data->pfds_index*3 + 1].events |= POLLIN;
448
449 poll_data->pfds[poll_data->pfds_index*3 + 2].fd = c->connection.ipmiconsole_fd;
450 poll_data->pfds[poll_data->pfds_index*3 + 2].events = 0;
451 poll_data->pfds[poll_data->pfds_index*3 + 2].revents = 0;
452 poll_data->pfds[poll_data->pfds_index*3 + 2].events |= POLLIN;
453 if (!scbuf_is_empty (c->connection.console_bmc_to_remote_console))
454 poll_data->pfds[poll_data->pfds_index*3 + 2].events |= POLLOUT;
455 }
456 else
457 {
458 poll_data->pfds[poll_data->pfds_index*3 + 1].fd = dummy_fd;
459 poll_data->pfds[poll_data->pfds_index*3 + 1].events = 0;
460 poll_data->pfds[poll_data->pfds_index*3 + 1].revents = 0;
461
462 poll_data->pfds[poll_data->pfds_index*3 + 2].fd = dummy_fd;
463 poll_data->pfds[poll_data->pfds_index*3 + 2].events = 0;
464 poll_data->pfds[poll_data->pfds_index*3 + 2].revents = 0;
465 }
466
467 poll_data->pfds_ctxs[poll_data->pfds_index] = c;
468
469 poll_data->pfds_index++;
470 return (0);
471 }
472
473 /*
474 * Return 0 on success
475 * Return -1 on fatal error
476 */
477 static int
_ipmi_recvfrom(ipmiconsole_ctx_t c)478 _ipmi_recvfrom (ipmiconsole_ctx_t c)
479 {
480 char buffer[IPMICONSOLE_PACKET_BUFLEN];
481 struct sockaddr_in6 from6;
482 struct sockaddr *from = (struct sockaddr *)&from6;
483 unsigned int fromlen = sizeof (struct sockaddr_in6);
484 ssize_t len;
485 int n, dropped = 0;
486 int secure_malloc_flag;
487
488 assert (c);
489 assert (c->magic == IPMICONSOLE_CTX_MAGIC);
490
491 secure_malloc_flag = (c->config.engine_flags & IPMICONSOLE_ENGINE_LOCK_MEMORY) ? 1 : 0;
492
493 do
494 {
495 /* For receive side, ipmi_lan_recvfrom and
496 * ipmi_rmcpplus_recvfrom are identical. So we just use
497 * ipmi_lan_recvfrom for both.
498 *
499 * In event of future change, should use util functions
500 * ipmi_is_ipmi_1_5_packet or ipmi_is_ipmi_2_0_packet
501 * appropriately.
502 */
503 len = ipmi_lan_recvfrom (c->connection.ipmi_fd,
504 buffer,
505 IPMICONSOLE_PACKET_BUFLEN,
506 0,
507 from,
508 &fromlen);
509 } while (len < 0 && errno == EINTR);
510
511 /* achu & hliebig:
512 *
513 * Premise from ipmitool (http://ipmitool.sourceforge.net/)
514 *
515 * On some OSes (it seems Unixes), the behavior is to not return
516 * port denied errors up to the client for UDP responses (i.e. you
517 * need to timeout). But on some OSes (it seems Windows), the
518 * behavior is to return port denied errors up to the user for UDP
519 * responses via ECONNRESET or ECONNREFUSED.
520 *
521 * If this were just the case, we could return or handle errors
522 * properly and move on. However, it's not the case.
523 *
524 * According to Ipmitool, on some motherboards, both the OS and the
525 * BMC are capable of responding to an IPMI request. That means you
526 * can get an ECONNRESET or ECONNREFUSED, then later on, get your
527 * real IPMI response.
528 *
529 * Our solution is copied from Ipmitool, we'll ignore some specific
530 * errors and try to read again.
531 *
532 * If the ECONNREFUSED or ECONNRESET is from the OS, but we will get
533 * an IPMI response later, the recvfrom later on gets the packet we
534 * want.
535 *
536 * If the ECONNREFUSED or ECONNRESET is from the OS but there is no
537 * BMC (or IPMI disabled, etc.), just do the recvfrom again to
538 * eventually get a timeout, which is the behavior we'd like.
539 */
540 if (len < 0
541 && (errno == ECONNRESET
542 || errno == ECONNREFUSED))
543 {
544 IPMICONSOLE_CTX_DEBUG (c, ("ipmi_lan_recvfrom: connection refused: %s", strerror (errno)));
545 return (0);
546 }
547
548 if (len < 0)
549 {
550 IPMICONSOLE_CTX_DEBUG (c, ("ipmi_lan_recvfrom: %s", strerror (errno)));
551 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_SYSTEM_ERROR);
552 return (-1);
553 }
554
555 if (!len)
556 {
557 IPMICONSOLE_CTX_DEBUG (c, ("ipmi_lan_recvfrom: no data", strerror (errno)));
558 /* Note: Not a fatal error, just return*/
559 return (0);
560 }
561
562 /* Sanity Check */
563 if (from6.sin6_family == AF_INET6)
564 {
565 if (memcmp (&from6.sin6_addr,
566 &(c->session.addr6.sin6_addr),
567 sizeof (from6.sin6_addr)))
568 {
569 IPMICONSOLE_CTX_DEBUG (c, ("received from invalid address"));
570 /* Note: Not a fatal error, just return */
571 return (0);
572 }
573 }
574 else
575 {
576 /* memcpy hacks to avoid warnings, i.e.
577 * warning: dereferencing pointer 'X' does break strict-aliasing rules
578 */
579 struct sockaddr_in from4;
580
581 memcpy (&from4, from, fromlen);
582
583 if (from4.sin_addr.s_addr != c->session.addr4.sin_addr.s_addr)
584 {
585 IPMICONSOLE_CTX_DEBUG (c, ("received from invalid address"));
586 /* Note: Not a fatal error, just return */
587 return (0);
588 }
589 }
590
591 /* Empty the scbuf if it's not empty */
592 if (!scbuf_is_empty (c->connection.ipmi_from_bmc))
593 {
594 IPMICONSOLE_CTX_DEBUG (c, ("ipmi_from_bmc not empty, draining"));
595 do {
596 char tempbuf[IPMICONSOLE_PACKET_BUFLEN];
597 if (scbuf_read (c->connection.ipmi_from_bmc, tempbuf, IPMICONSOLE_PACKET_BUFLEN) < 0)
598 {
599 IPMICONSOLE_CTX_DEBUG (c, ("scbuf_read: %s", strerror (errno)));
600 break;
601 }
602 } while(!scbuf_is_empty (c->connection.ipmi_from_bmc));
603 }
604
605 if ((n = scbuf_write (c->connection.ipmi_from_bmc, buffer, len, &dropped, secure_malloc_flag)) < 0)
606 {
607 IPMICONSOLE_CTX_DEBUG (c, ("scbuf_write: %s", strerror (errno)));
608 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
609 return (-1);
610 }
611
612 if (n != len)
613 {
614 IPMICONSOLE_CTX_DEBUG (c, ("scbuf_write: invalid bytes written; n=%d; len=%d", n, len));
615 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
616 return (-1);
617 }
618
619 if (dropped)
620 {
621 IPMICONSOLE_CTX_DEBUG (c, ("scbuf_write: dropped data: dropped=%d", dropped));
622 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
623 return (-1);
624 }
625
626 return (0);
627 }
628
629 /*
630 * Return 0 on success
631 * Return -1 on fatal error
632 */
633 static int
_ipmi_sendto(ipmiconsole_ctx_t c)634 _ipmi_sendto (ipmiconsole_ctx_t c)
635 {
636 char buffer[IPMICONSOLE_PACKET_BUFLEN];
637 ssize_t len;
638 int n;
639
640 assert (c);
641 assert (c->magic == IPMICONSOLE_CTX_MAGIC);
642
643 if ((n = scbuf_read (c->connection.ipmi_to_bmc, buffer, IPMICONSOLE_PACKET_BUFLEN)) < 0)
644 {
645 IPMICONSOLE_CTX_DEBUG (c, ("scbuf_read: %s", strerror (errno)));
646 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
647 return (-1);
648 }
649
650 if (ipmi_is_ipmi_1_5_packet (buffer, n))
651 {
652 do
653 {
654 len = ipmi_lan_sendto (c->connection.ipmi_fd,
655 buffer,
656 n,
657 0,
658 c->session.addr,
659 c->session.addr_len);
660 } while (len < 0 && errno == EINTR);
661 }
662 else
663 {
664 do
665 {
666 len = ipmi_rmcpplus_sendto (c->connection.ipmi_fd,
667 buffer,
668 n,
669 0,
670 c->session.addr,
671 c->session.addr_len);
672 } while (len < 0 && errno == EINTR);
673 }
674
675 if (len < 0)
676 {
677 IPMICONSOLE_CTX_DEBUG (c, ("ipmi_lan_sendto: %s", strerror (errno)));
678 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_SYSTEM_ERROR);
679 return (-1);
680 }
681
682 #if 0
683 /* don't check, let bad packet timeout */
684 if (len != n)
685 {
686 IPMICONSOLE_CTX_DEBUG (c, ("ipmi_lan_sendto: invalid bytes written; n=%d; len=%d", n, len));
687 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_SYSTEM_ERROR);
688 return (-1);
689 }
690 #endif
691
692 /* scbuf should be empty now */
693 if (!scbuf_is_empty (c->connection.ipmi_to_bmc))
694 {
695 IPMICONSOLE_CTX_DEBUG (c, ("ipmi_to_bmc not empty"));
696 /* Note: Not a fatal error, just return*/
697 return (0);
698 }
699
700 return (0);
701 }
702
703 /*
704 * Return 0 on success
705 * Return -1 on fatal error
706 */
707 static int
_asynccomm(ipmiconsole_ctx_t c)708 _asynccomm (ipmiconsole_ctx_t c)
709 {
710 uint8_t tmpbyte;
711 ssize_t len;
712
713 assert (c);
714 assert (c->magic == IPMICONSOLE_CTX_MAGIC);
715
716 if ((len = read (c->connection.asynccomm[0], (void *)&tmpbyte, 1)) < 0)
717 {
718 IPMICONSOLE_CTX_DEBUG (c, ("read: %s", strerror (errno)));
719 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_SYSTEM_ERROR);
720 return (-1);
721 }
722
723 if (!len)
724 {
725 IPMICONSOLE_CTX_DEBUG (c, ("asynccomm closed"));
726 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
727 return (-1);
728 }
729
730 /* User may have requested several break conditions in a
731 * row quickly. We assume it means just one
732 */
733 if (tmpbyte == IPMICONSOLE_PIPE_GENERATE_BREAK_CODE)
734 {
735 if (!(c->session.break_requested))
736 {
737 int bytes_before_break;
738
739 c->session.break_requested++;
740
741 if ((bytes_before_break = scbuf_used (c->connection.console_remote_console_to_bmc)) < 0)
742 {
743 IPMICONSOLE_CTX_DEBUG (c, ("scbuf_used: %s", strerror (errno)));
744 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
745 return (-1);
746 }
747 c->session.console_remote_console_to_bmc_bytes_before_break = bytes_before_break;
748 }
749 }
750
751 return (0);
752 }
753
754 /*
755 * Return 0 on success
756 * Return -1 on fatal error
757 */
758 static int
_console_read(ipmiconsole_ctx_t c)759 _console_read (ipmiconsole_ctx_t c)
760 {
761 char buffer[IPMICONSOLE_PACKET_BUFLEN];
762 ssize_t len;
763 int n, dropped = 0;
764 int secure_malloc_flag;
765
766 assert (c);
767 assert (c->magic == IPMICONSOLE_CTX_MAGIC);
768 assert (!c->session.close_session_flag);
769
770 secure_malloc_flag = (c->config.engine_flags & IPMICONSOLE_ENGINE_LOCK_MEMORY) ? 1 : 0;
771
772 if ((len = read (c->connection.ipmiconsole_fd,
773 buffer,
774 IPMICONSOLE_PACKET_BUFLEN)) < 0)
775 {
776 IPMICONSOLE_CTX_DEBUG (c, ("read: %s", strerror (errno)));
777 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_SYSTEM_ERROR);
778 return (-1);
779 }
780
781 if (!len)
782 {
783 /* Returning -1 closes the session, but really this error is ok
784 * since the user is allowed to close the session
785 */
786 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_SUCCESS);
787 return (-1);
788 }
789
790 if ((n = scbuf_write (c->connection.console_remote_console_to_bmc, buffer, len, &dropped, secure_malloc_flag)) < 0)
791 {
792 IPMICONSOLE_CTX_DEBUG (c, ("scbuf_write: %s", strerror (errno)));
793 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
794 return (-1);
795 }
796
797 if (n != len)
798 {
799 IPMICONSOLE_CTX_DEBUG (c, ("scbuf_write: invalid bytes written; n=%d; len=%d", n, len));
800 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
801 return (-1);
802 }
803
804 if (dropped)
805 {
806 IPMICONSOLE_CTX_DEBUG (c, ("scbuf_write: dropped data: dropped=%d", dropped));
807 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
808 return (-1);
809 }
810
811 return (0);
812 }
813
814 /*
815 * Return 0 on success
816 * Return -1 on fatal error
817 */
818 static int
_console_write(ipmiconsole_ctx_t c)819 _console_write (ipmiconsole_ctx_t c)
820 {
821 char buffer[IPMICONSOLE_PACKET_BUFLEN];
822 ssize_t len;
823 int n;
824
825 assert (c);
826 assert (c->magic == IPMICONSOLE_CTX_MAGIC);
827 assert (!c->session.close_session_flag);
828
829 /*
830 * XXX: Shouldn't assume user will read data fast enough? I could
831 * overrun buffers?
832 *
833 * Deal with it later.
834 */
835
836 if ((n = scbuf_read (c->connection.console_bmc_to_remote_console, buffer, IPMICONSOLE_PACKET_BUFLEN)) < 0)
837 {
838 IPMICONSOLE_CTX_DEBUG (c, ("scbuf_read: %s", strerror (errno)));
839 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
840 return (-1);
841 }
842
843 if ((len = write (c->connection.ipmiconsole_fd,
844 buffer,
845 n)) < 0)
846 {
847 IPMICONSOLE_CTX_DEBUG (c, ("write: %s", strerror (errno)));
848
849 if (errno == EPIPE)
850 {
851 /* This error is ok since the user is allowed to close the
852 * session
853 */
854 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_SUCCESS);
855 }
856 else
857 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_SYSTEM_ERROR);
858 return (-1);
859 }
860
861 if (len != n)
862 {
863 IPMICONSOLE_CTX_DEBUG (c, ("write: invalid bytes written; n=%d; len=%d", n, len));
864 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
865 return (-1);
866 }
867
868 /* scbuf should be empty now */
869 if (!scbuf_is_empty (c->connection.console_bmc_to_remote_console))
870 {
871 IPMICONSOLE_CTX_DEBUG (c, ("console_bmc_to_remote_console not empty"));
872 /* Note: Not a fatal error, just return*/
873 return (0);
874 }
875
876 return (0);
877 }
878
879 static int
_ipmiconsole_poll(struct pollfd * ufds,unsigned int nfds,int timeout)880 _ipmiconsole_poll (struct pollfd *ufds, unsigned int nfds, int timeout)
881 {
882 int n;
883 struct timeval tv, tv_orig;
884 struct timeval start, end, delta;
885
886 assert (ufds);
887
888 /* prep for EINTR handling */
889 if (timeout >= 0)
890 {
891 /* poll uses timeout in milliseconds */
892 tv_orig.tv_sec = (long)timeout/1000;
893 tv_orig.tv_usec = (timeout % 1000) * 1000;
894
895 if (gettimeofday(&start, NULL) < 0)
896 {
897 IPMICONSOLE_DEBUG (("gettimeofday: %s", strerror (errno)));
898 return (-1);
899 }
900 }
901 else
902 {
903 tv_orig.tv_sec = 0;
904 tv_orig.tv_usec = 0;
905 }
906
907 /* repeat poll if interrupted */
908 do
909 {
910 n = poll(ufds, nfds, timeout);
911
912 if (n < 0 && errno != EINTR) /* unrecov error */
913 {
914 IPMICONSOLE_DEBUG (("poll: %s", strerror (errno)));
915 return (-1);
916 }
917
918 if (n < 0 && timeout >= 0) /* EINTR - adjust timeout */
919 {
920 if (gettimeofday(&end, NULL) < 0)
921 {
922 IPMICONSOLE_DEBUG (("gettimeofday: %s", strerror (errno)));
923 return (-1);
924 }
925
926 timersub(&end, &start, &delta); /* delta = end-start */
927 timersub(&tv_orig, &delta, &tv); /* tv = tvsave-delta */
928 timeout = (tv.tv_sec * 1000) + (tv.tv_usec/1000);
929 }
930 } while (n < 0);
931
932 return n;
933 }
934
935 static void *
_ipmiconsole_engine(void * arg)936 _ipmiconsole_engine (void *arg)
937 {
938 int perr, ctxs_count = 0;
939 unsigned int index;
940 unsigned int teardown_flag = 0;
941 unsigned int teardown_initiated = 0;
942
943 assert (arg);
944
945 index = *((unsigned int *)arg);
946
947 assert (index < IPMICONSOLE_THREAD_COUNT_MAX);
948
949 free (arg);
950
951 /* No need to exit on failure, probability is low we'll SIGPIPE anyways */
952 if (signal (SIGPIPE, SIG_IGN) == SIG_ERR)
953 IPMICONSOLE_DEBUG (("signal: %s", strerror (errno)));
954
955 while (!teardown_flag || ctxs_count)
956 {
957 struct _ipmiconsole_poll_data poll_data;
958 int count;
959 unsigned int timeout_len;
960 unsigned int i;
961 int unlock_console_engine_ctxs_mutex_flag = 0;
962 int spin_wait_flag = 0;
963 char buf[IPMICONSOLE_PIPE_BUFLEN];
964
965 poll_data.pfds = NULL;
966 poll_data.pfds_ctxs = NULL;
967 poll_data.ctxs_len = 0;
968 poll_data.pfds_index = 0;
969
970 if ((perr = pthread_mutex_lock (&console_engine_teardown_mutex)))
971 {
972 /* This is one of the only truly "fatal" conditions */
973 IPMICONSOLE_DEBUG (("pthread_mutex_lock: %s", strerror (perr)));
974 teardown_flag = 1;
975 }
976
977 if (console_engine_teardown_immediate)
978 {
979 if ((perr = pthread_mutex_unlock (&console_engine_teardown_mutex)))
980 IPMICONSOLE_DEBUG (("pthread_mutex_unlock: %s", strerror (perr)));
981 break;
982 }
983
984 if (console_engine_teardown)
985 teardown_flag = 1;
986
987 if ((perr = pthread_mutex_unlock (&console_engine_teardown_mutex)))
988 {
989 /* This is one of the only truly "fatal" conditions */
990 IPMICONSOLE_DEBUG (("pthread_mutex_unlock: %s", strerror (perr)));
991 teardown_flag = 1;
992 }
993
994 /* Notes:
995 *
996 * We must lock the list from here till all context data and pointers
997 * are retrieved.
998 */
999
1000 if ((perr = pthread_mutex_lock (&console_engine_ctxs_mutex[index])))
1001 {
1002 /* This is one of the only truly "fatal" conditions */
1003 IPMICONSOLE_DEBUG (("pthread_mutex_lock: %s", strerror (perr)));
1004 teardown_flag = 1;
1005 }
1006
1007 /* Note: Set close_session_flag in the contexts before
1008 * ipmiconsole_process_ctxs(), so the initiation of the closing
1009 * down will begin now rather than the next iteration of the
1010 * loop.
1011 */
1012 if (teardown_flag && !teardown_initiated)
1013 {
1014 /* XXX: Umm, if this fails, we may not be able to teardown
1015 * cleanly. Break out of the loop I guess.
1016 */
1017 if (list_for_each (console_engine_ctxs[index], _teardown_initiate, NULL) < 0)
1018 {
1019 IPMICONSOLE_DEBUG (("list_for_each: %s", strerror (errno)));
1020 break;
1021 }
1022 teardown_initiated++;
1023 }
1024
1025 if ((ctxs_count = ipmiconsole_process_ctxs (console_engine_ctxs[index], &timeout_len)) < 0)
1026 goto continue_loop;
1027
1028 if (!ctxs_count && teardown_flag)
1029 continue;
1030
1031 if (!ctxs_count)
1032 {
1033 spin_wait_flag++;
1034 goto continue_loop;
1035 }
1036 poll_data.ctxs_len = ctxs_count;
1037
1038 /* achu: I always wonder if this poll() loop could be done far
1039 * more elegantly and efficiently without all this crazy
1040 * indexing, perhaps through a callback/event mechanism. It'd
1041 * probably be more efficient, since most callback/event based
1042 * models have min-heap like structures inside for determining
1043 * what things timed out. Overall though, I don't think the O(n)
1044 * (n being hosts/fds) processing is really that inefficient for
1045 * this particular application and is not worth going back and
1046 * changing. By going to a callback/event mechanism, there will
1047 * still be some O(n) activities within the code, so I am only
1048 * going to create a more efficient O(n) poll loop.
1049 */
1050
1051 /*
1052 * There are 3 pfds per ctx. One for 'ipmi_fd', 'asynccomm[0]', and 'ipmiconsole_fd'.
1053 *
1054 * There is + 1 pfds for the "console_engine_ctxs_notifier".
1055 * This will be set up manually here, and not in _poll_setup().
1056 */
1057 if (!(poll_data.pfds = (struct pollfd *)malloc (((poll_data.ctxs_len * 3) + 1) * sizeof (struct pollfd))))
1058 {
1059 IPMICONSOLE_DEBUG (("malloc: %s", strerror (errno)));
1060 goto continue_loop;
1061 }
1062
1063 if (!(poll_data.pfds_ctxs = (ipmiconsole_ctx_t *)malloc (poll_data.ctxs_len * sizeof (ipmiconsole_ctx_t))))
1064 {
1065 IPMICONSOLE_DEBUG (("malloc: %s", strerror (errno)));
1066 goto continue_loop;
1067 }
1068
1069 if ((count = list_for_each (console_engine_ctxs[index], _poll_setup, &poll_data)) < 0)
1070 {
1071 IPMICONSOLE_DEBUG (("list_for_each: %s", strerror (errno)));
1072 goto continue_loop;
1073 }
1074
1075 if ((perr = pthread_mutex_unlock (&console_engine_ctxs_mutex[index])))
1076 IPMICONSOLE_DEBUG (("pthread_mutex_unlock: %s", strerror (perr)));
1077 unlock_console_engine_ctxs_mutex_flag++;
1078
1079 /* Setup notifier pipe as last remaining poll data */
1080 poll_data.pfds[(poll_data.ctxs_len * 3)].fd = console_engine_ctxs_notifier[index][0];
1081 poll_data.pfds[(poll_data.ctxs_len * 3)].events = POLLIN;
1082 poll_data.pfds[(poll_data.ctxs_len * 3)].revents = 0;
1083
1084 if (count != ctxs_count)
1085 {
1086 IPMICONSOLE_DEBUG (("list_for_each: invalid length returned: %d", count));
1087 goto continue_loop;
1088 }
1089
1090 if (poll_data.pfds_index != ctxs_count)
1091 {
1092 IPMICONSOLE_DEBUG (("invalid index set on returned: %d", poll_data.pfds_index));
1093 goto continue_loop;
1094 }
1095
1096 if (_ipmiconsole_poll (poll_data.pfds, (poll_data.ctxs_len * 3) + 1, timeout_len) < 0)
1097 {
1098 IPMICONSOLE_DEBUG (("poll: %s", strerror (errno)));
1099 goto continue_loop;
1100 }
1101
1102 for (i = 0; i < poll_data.ctxs_len; i++)
1103 {
1104 if (poll_data.pfds[i*3].revents & POLLERR)
1105 {
1106 IPMICONSOLE_CTX_DEBUG (poll_data.pfds_ctxs[i], ("POLLERR"));
1107 /* See comments in _ipmi_recvfrom() regarding ECONNRESET/ECONNREFUSED */
1108 if (_ipmi_recvfrom (poll_data.pfds_ctxs[i]) < 0)
1109 {
1110 ipmiconsole_ctx_set_errnum (poll_data.pfds_ctxs[i], IPMICONSOLE_ERR_SYSTEM_ERROR);
1111 poll_data.pfds_ctxs[i]->session.close_session_flag++;
1112 continue;
1113 }
1114 }
1115 if (!poll_data.pfds_ctxs[i]->session.close_session_flag)
1116 {
1117 if (poll_data.pfds[i*3+1].revents & POLLNVAL)
1118 {
1119 /* This indicates the user closed the asynccomm file descriptors
1120 * which is ok.
1121 */
1122 IPMICONSOLE_CTX_DEBUG (poll_data.pfds_ctxs[i], ("POLLNVAL"));
1123 ipmiconsole_ctx_set_errnum (poll_data.pfds_ctxs[i], IPMICONSOLE_ERR_SUCCESS);
1124 poll_data.pfds_ctxs[i]->session.close_session_flag++;
1125 continue;
1126 }
1127 if (poll_data.pfds[i*3+2].revents & POLLHUP)
1128 {
1129 /* This indicates the user closed the other end of
1130 * the socketpair so it's ok.
1131 */
1132 IPMICONSOLE_CTX_DEBUG (poll_data.pfds_ctxs[i], ("POLLHUP"));
1133 ipmiconsole_ctx_set_errnum (poll_data.pfds_ctxs[i], IPMICONSOLE_ERR_SUCCESS);
1134 poll_data.pfds_ctxs[i]->session.close_session_flag++;
1135 continue;
1136 }
1137 if (poll_data.pfds[i*3+1].revents & POLLERR)
1138 {
1139 IPMICONSOLE_CTX_DEBUG (poll_data.pfds_ctxs[i], ("POLLERR"));
1140 ipmiconsole_ctx_set_errnum (poll_data.pfds_ctxs[i], IPMICONSOLE_ERR_INTERNAL_ERROR);
1141 poll_data.pfds_ctxs[i]->session.close_session_flag++;
1142 continue;
1143 }
1144 if (poll_data.pfds[i*3+2].revents & POLLERR)
1145 {
1146 IPMICONSOLE_CTX_DEBUG (poll_data.pfds_ctxs[i], ("POLLERR"));
1147 ipmiconsole_ctx_set_errnum (poll_data.pfds_ctxs[i], IPMICONSOLE_ERR_INTERNAL_ERROR);
1148 poll_data.pfds_ctxs[i]->session.close_session_flag++;
1149 continue;
1150 }
1151 }
1152 if (poll_data.pfds[i*3].revents & POLLIN)
1153 {
1154 if (_ipmi_recvfrom (poll_data.pfds_ctxs[i]) < 0)
1155 {
1156 poll_data.pfds_ctxs[i]->session.close_session_flag++;
1157 continue;
1158 }
1159 }
1160 if (poll_data.pfds[i*3].revents & POLLOUT)
1161 {
1162 if (_ipmi_sendto (poll_data.pfds_ctxs[i]) < 0)
1163 {
1164 poll_data.pfds_ctxs[i]->session.close_session_flag++;
1165 continue;
1166 }
1167 }
1168 if (poll_data.pfds[i*3 + 1].revents & POLLIN)
1169 {
1170 if (_asynccomm (poll_data.pfds_ctxs[i]) < 0)
1171 {
1172 poll_data.pfds_ctxs[i]->session.close_session_flag++;
1173 continue;
1174 }
1175 }
1176 if (!poll_data.pfds_ctxs[i]->session.close_session_flag)
1177 {
1178 if (poll_data.pfds[i*3+2].revents & POLLIN)
1179 {
1180 if (_console_read (poll_data.pfds_ctxs[i]) < 0)
1181 {
1182 poll_data.pfds_ctxs[i]->session.close_session_flag++;
1183 continue;
1184 }
1185 }
1186 if (poll_data.pfds[i*3+2].revents & POLLOUT)
1187 {
1188 if (_console_write (poll_data.pfds_ctxs[i]) < 0)
1189 {
1190 poll_data.pfds_ctxs[i]->session.close_session_flag++;
1191 continue;
1192 }
1193 }
1194 }
1195 }
1196
1197 /* We don't care what's read, just get it off the fd */
1198 if (poll_data.pfds[(poll_data.ctxs_len * 3)].revents & POLLIN)
1199 {
1200 if (read (console_engine_ctxs_notifier[index][0], buf, IPMICONSOLE_PIPE_BUFLEN) < 0)
1201 IPMICONSOLE_DEBUG (("read: %s", strerror (errno)));
1202 }
1203
1204 continue_loop:
1205 if (!unlock_console_engine_ctxs_mutex_flag)
1206 {
1207 if ((perr = pthread_mutex_unlock (&console_engine_ctxs_mutex[index])))
1208 {
1209 /* This is one of the only truly "fatal" conditions */
1210 IPMICONSOLE_DEBUG (("pthread_mutex_unlock: %s", strerror (perr)));
1211 teardown_flag = 1;
1212 }
1213 }
1214 if (spin_wait_flag)
1215 {
1216 /* No contexts stored, either because they all died or none
1217 * have been submitted yet. Sleep a little bit to kill some
1218 * time and avoid spinning.
1219 */
1220 /* XXX: Is this portable? */
1221 usleep (IPMICONSOLE_SPIN_WAIT_TIME);
1222 }
1223 free (poll_data.pfds);
1224 free (poll_data.pfds_ctxs);
1225 }
1226
1227 /* No way to return error, so just continue on even if there is a failure */
1228 if ((perr = pthread_mutex_lock (&console_engine_thread_count_mutex)))
1229 IPMICONSOLE_DEBUG (("pthread_mutex_lock: %s", strerror (perr)));
1230
1231 console_engine_thread_count--;
1232
1233 if ((perr = pthread_mutex_unlock (&console_engine_thread_count_mutex)))
1234 IPMICONSOLE_DEBUG (("pthread_mutex_unlock: %s", strerror (perr)));
1235
1236 return (NULL);
1237 }
1238
1239 /* Notes: On an error, it is the responsibility of the caller to call
1240 * ipmiconsole_engine_cleanup() to destroy all previously created
1241 * threads.
1242 */
1243 int
ipmiconsole_engine_thread_create(void)1244 ipmiconsole_engine_thread_create (void)
1245 {
1246 pthread_t thread;
1247 pthread_attr_t attr;
1248 unsigned int *index = NULL;
1249 int perr, rv = -1;
1250
1251 assert (console_engine_is_setup);
1252
1253 if ((perr = pthread_mutex_lock (&console_engine_thread_count_mutex)))
1254 {
1255 IPMICONSOLE_DEBUG (("pthread_mutex_lock: %s", strerror (perr)));
1256 errno = perr;
1257 return (-1);
1258 }
1259
1260 assert (console_engine_thread_count < IPMICONSOLE_THREAD_COUNT_MAX);
1261
1262 if ((perr = pthread_attr_init (&attr)))
1263 {
1264 IPMICONSOLE_DEBUG (("pthread_attr_init: %s", strerror (perr)));
1265 errno = perr;
1266 goto cleanup;
1267 }
1268
1269 if ((perr = pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED)))
1270 {
1271 IPMICONSOLE_DEBUG (("pthread_attr_setdetachstate: %s", strerror (perr)));
1272 errno = perr;
1273 goto cleanup;
1274 }
1275
1276 if (!(index = (unsigned int *)malloc (sizeof (unsigned int))))
1277 {
1278 IPMICONSOLE_DEBUG (("malloc: %s", strerror (errno)));
1279 goto cleanup;
1280 }
1281 *index = console_engine_thread_count;
1282
1283 if ((perr = pthread_create (&thread, &attr, _ipmiconsole_engine, index)))
1284 {
1285 IPMICONSOLE_DEBUG (("pthread_create: %s", strerror (perr)));
1286 errno = perr;
1287 goto cleanup;
1288 }
1289
1290 /* Who cares if this fails */
1291 if ((perr = pthread_attr_destroy (&attr)))
1292 IPMICONSOLE_DEBUG (("pthread_attr_destroy: %s", strerror (perr)));
1293
1294 console_engine_thread_count++;
1295
1296 rv = 0;
1297 cleanup:
1298
1299 if ((perr = pthread_mutex_unlock (&console_engine_thread_count_mutex)))
1300 {
1301 IPMICONSOLE_DEBUG (("pthread_mutex_unlock: %s", strerror (perr)));
1302 errno = perr;
1303 return (-1);
1304 }
1305
1306 return (rv);
1307 }
1308
1309 int
ipmiconsole_engine_submit_ctx(ipmiconsole_ctx_t c)1310 ipmiconsole_engine_submit_ctx (ipmiconsole_ctx_t c)
1311 {
1312 void *ptr;
1313 unsigned int i;
1314 int perr, ret = -1;
1315 unsigned int min_submitted = UINT_MAX;
1316 int index = 0;
1317
1318 assert (c);
1319 assert (c->magic == IPMICONSOLE_CTX_MAGIC);
1320 assert (!(c->session_submitted));
1321 assert (console_engine_is_setup);
1322
1323 if ((perr = pthread_mutex_lock (&console_engine_thread_count_mutex)))
1324 {
1325 IPMICONSOLE_DEBUG (("pthread_mutex_lock: %s", strerror (perr)));
1326 return (-1);
1327 }
1328
1329 for (i = 0; i < console_engine_thread_count; i++)
1330 {
1331 if ((perr = pthread_mutex_lock (&console_engine_ctxs_mutex[i])))
1332 {
1333 IPMICONSOLE_DEBUG (("pthread_mutex_lock: %s", strerror (perr)));
1334 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
1335 goto cleanup_thread_count;
1336 }
1337
1338 if (console_engine_ctxs_count[i] < min_submitted)
1339 {
1340 min_submitted = console_engine_ctxs_count[i];
1341 index = i;
1342 }
1343
1344 if ((perr = pthread_mutex_unlock (&console_engine_ctxs_mutex[i])))
1345 {
1346 IPMICONSOLE_DEBUG (("pthread_mutex_unlock: %s", strerror (perr)));
1347 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
1348 goto cleanup_thread_count;
1349 }
1350 }
1351
1352 if ((perr = pthread_mutex_lock (&console_engine_ctxs_mutex[index])))
1353 {
1354 IPMICONSOLE_DEBUG (("pthread_mutex_lock: %s", strerror (perr)));
1355 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
1356 goto cleanup_thread_count;
1357 }
1358
1359 if (!(ptr = list_append (console_engine_ctxs[index], c)))
1360 {
1361 /* Note: Don't do a CTX debug, this is more of a global debug */
1362 IPMICONSOLE_DEBUG (("list_append: %s", strerror (errno)));
1363 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
1364 goto cleanup_ctxs;
1365 }
1366
1367 if (ptr != (void *)c)
1368 {
1369 IPMICONSOLE_DEBUG (("list_append: invalid pointer: ptr=%p; c=%p", ptr, c));
1370 ipmiconsole_ctx_set_errnum (c, IPMICONSOLE_ERR_INTERNAL_ERROR);
1371 goto cleanup_ctxs;
1372 }
1373
1374 console_engine_ctxs_count[index]++;
1375
1376 ret = 0;
1377
1378 /* achu:
1379 *
1380 * Necessary to set this here b/c at this point in time, the context
1381 * is submitted, so the engine will be doing its own cleanup
1382 * (garbage collector, etc.).
1383 */
1384 c->session_submitted++;
1385
1386 if ((perr = pthread_mutex_lock (&(c->signal.mutex_ctx_state))) != 0)
1387 IPMICONSOLE_DEBUG (("pthread_mutex_lock: %s", strerror (perr)));
1388
1389 c->signal.ctx_state = IPMICONSOLE_CTX_STATE_ENGINE_SUBMITTED;
1390
1391 if ((perr = pthread_mutex_unlock (&(c->signal.mutex_ctx_state))) != 0)
1392 IPMICONSOLE_DEBUG (("pthread_mutex_unlock: %s", strerror (perr)));
1393
1394 /* "Interrupt" the engine and tell it to get moving along w/ the new context */
1395 if (write (console_engine_ctxs_notifier[index][1], "1", 1) < 0)
1396 IPMICONSOLE_DEBUG (("write: %s", strerror (errno)));
1397
1398 cleanup_ctxs:
1399 if ((perr = pthread_mutex_unlock (&console_engine_ctxs_mutex[index])))
1400 {
1401 IPMICONSOLE_DEBUG (("pthread_mutex_unlock: %s", strerror (perr)));
1402 goto cleanup_thread_count;
1403 }
1404
1405 cleanup_thread_count:
1406 if ((perr = pthread_mutex_unlock (&console_engine_thread_count_mutex)))
1407 IPMICONSOLE_DEBUG (("pthread_mutex_unlock: %s", strerror (perr)));
1408
1409 return (ret);
1410 }
1411
1412 int
ipmiconsole_engine_cleanup(int cleanup_sol_sessions)1413 ipmiconsole_engine_cleanup (int cleanup_sol_sessions)
1414 {
1415 unsigned int i;
1416 unsigned int thread_count;
1417 int perr, rv = -1;
1418
1419 if ((perr = pthread_mutex_lock (&console_engine_is_setup_mutex)))
1420 {
1421 IPMICONSOLE_DEBUG (("pthread_mutex_lock: %s", strerror (perr)));
1422 return (-1);
1423 }
1424
1425 if (!console_engine_is_setup)
1426 goto unlock_is_setup_mutex;
1427
1428 if ((perr = pthread_mutex_lock (&console_engine_thread_count_mutex)))
1429 {
1430 IPMICONSOLE_DEBUG (("pthread_mutex_lock: %s", strerror (perr)));
1431 goto unlock_is_setup_mutex;
1432 }
1433
1434 thread_count = console_engine_thread_count;
1435
1436 if ((perr = pthread_mutex_unlock (&console_engine_thread_count_mutex)))
1437 {
1438 IPMICONSOLE_DEBUG (("pthread_mutex_unlock: %s", strerror (perr)));
1439 goto unlock_is_setup_mutex;
1440 }
1441
1442 if (!thread_count)
1443 {
1444 rv = 0;
1445 goto engine_cleanup;
1446 }
1447
1448 if ((perr = pthread_mutex_lock (&console_engine_teardown_mutex)))
1449 {
1450 IPMICONSOLE_DEBUG (("pthread_mutex_lock: %s", strerror (perr)));
1451 goto engine_cleanup;
1452 }
1453
1454 console_engine_teardown++;
1455 if (!cleanup_sol_sessions)
1456 console_engine_teardown_immediate++;
1457
1458 if ((perr = pthread_mutex_unlock (&console_engine_teardown_mutex)))
1459 {
1460 IPMICONSOLE_DEBUG (("pthread_mutex_unlock: %s", strerror (perr)));
1461 goto engine_cleanup;
1462 }
1463
1464 /* "Interrupt" the engine thread and tell it to get moving along */
1465 for (i = 0; i < console_engine_ctxs_notifier_num; i++)
1466 {
1467 if (write (console_engine_ctxs_notifier[i][1], "1", 1) < 0)
1468 IPMICONSOLE_DEBUG (("write: %s", strerror (errno)));
1469 }
1470
1471 if ((perr = pthread_mutex_lock (&console_engine_thread_count_mutex)))
1472 {
1473 IPMICONSOLE_DEBUG (("pthread_mutex_lock: %s", strerror (perr)));
1474 goto engine_cleanup;
1475 }
1476
1477 while (console_engine_thread_count)
1478 {
1479 if ((perr = pthread_mutex_unlock (&console_engine_thread_count_mutex)))
1480 {
1481 IPMICONSOLE_DEBUG (("pthread_mutex_unlock: %s", strerror (perr)));
1482 goto engine_cleanup;
1483 }
1484
1485 /* Wait awhile then try again */
1486 /* XXX: Is this portable? */
1487 usleep (IPMICONSOLE_SPIN_WAIT_TIME);
1488
1489 if ((perr = pthread_mutex_lock (&console_engine_thread_count_mutex)))
1490 {
1491 IPMICONSOLE_DEBUG (("pthread_mutex_lock: %s", strerror (perr)));
1492 goto engine_cleanup;
1493 }
1494 }
1495
1496 if ((perr = pthread_mutex_unlock (&console_engine_thread_count_mutex)))
1497 {
1498 IPMICONSOLE_DEBUG (("pthread_mutex_unlock: %s", strerror (perr)));
1499 goto engine_cleanup;
1500 }
1501
1502 /* "Interrupt" the garbage collector and tell it to quit */
1503 if (write (garbage_collector_notifier[1], "1", 1) < 0)
1504 IPMICONSOLE_DEBUG (("write: %s", strerror (errno)));
1505
1506 if ((perr = pthread_mutex_lock (&garbage_collector_active_mutex)))
1507 {
1508 IPMICONSOLE_DEBUG (("pthread_mutex_lock: %s", strerror (perr)));
1509 goto engine_cleanup;
1510 }
1511
1512 while (garbage_collector_active)
1513 {
1514 if ((perr = pthread_cond_wait (&garbage_collector_active_cond,
1515 &garbage_collector_active_mutex)))
1516 {
1517 IPMICONSOLE_DEBUG (("pthread_cond_wait: %s", strerror (perr)));
1518 goto engine_cleanup;
1519 }
1520 }
1521
1522 if ((perr = pthread_mutex_unlock (&garbage_collector_active_mutex)))
1523 {
1524 IPMICONSOLE_DEBUG (("pthread_mutex_unlock: %s", strerror (perr)));
1525 goto engine_cleanup;
1526 }
1527
1528 engine_cleanup:
1529 for (i = 0; i < IPMICONSOLE_THREAD_COUNT_MAX; i++)
1530 {
1531 if (console_engine_ctxs[i])
1532 list_destroy (console_engine_ctxs[i]);
1533 console_engine_ctxs[i] = NULL;
1534 console_engine_ctxs[i] = 0;
1535 pthread_mutex_destroy (&console_engine_ctxs_mutex[i]);
1536 /* ignore potential error, cleanup path */
1537 close (console_engine_ctxs_notifier[i][0]);
1538 /* ignore potential error, cleanup path */
1539 close (console_engine_ctxs_notifier[i][1]);
1540 }
1541 /* ignore potential error, cleanup path */
1542 close (garbage_collector_notifier[0]);
1543 /* ignore potential error, cleanup path */
1544 close (garbage_collector_notifier[1]);
1545
1546 /* achu: The engine threads have been torn down, all the contexts
1547 * managed by those threads have been moved to
1548 * console_engine_ctxs_to_destroy, and the garbage collector has
1549 * been shut down. So we don't need to lock w/ the
1550 * console_engine_ctxs_to_destroy_mutex.
1551 *
1552 * This list destruction will cleanup everything except the minimal
1553 * data within a context for the user to destroy with
1554 * ipmiconsole_ctx_destroy.
1555 */
1556
1557 list_destroy (console_engine_ctxs_to_destroy);
1558 console_engine_ctxs_to_destroy = NULL;
1559
1560 /* ignore potential error, cleanup path */
1561 close (dummy_fd);
1562 dummy_fd = -1;
1563
1564 console_engine_is_setup = 0;
1565
1566 if ((perr = pthread_mutex_lock (&console_engine_teardown_mutex)))
1567 {
1568 IPMICONSOLE_DEBUG (("pthread_mutex_lock: %s", strerror (perr)));
1569 goto unlock_is_setup_mutex;
1570 }
1571
1572 console_engine_teardown = 0;
1573 console_engine_teardown_immediate = 0;
1574
1575 if ((perr = pthread_mutex_unlock (&console_engine_teardown_mutex)))
1576 {
1577 IPMICONSOLE_DEBUG (("pthread_mutex_unlock: %s", strerror (perr)));
1578 goto unlock_is_setup_mutex;
1579 }
1580
1581 rv = 0;
1582 unlock_is_setup_mutex:
1583 if ((perr = pthread_mutex_unlock (&console_engine_is_setup_mutex)))
1584 IPMICONSOLE_DEBUG (("pthread_mutex_unlock: %s", strerror (perr)));
1585
1586 return (rv);
1587 }
1588