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