Use thread-safe libgcrypt and libgpg-error functions. Only call the new
[pwmd.git] / src / status.c
blob0b0c338b7210845aa2edff37fc7a26028956ac6d
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006-2009 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
19 #include <glib.h>
20 #include "common.h"
21 #include "lock.h"
22 #include "misc.h"
23 #include "status.h"
24 #include "pwmd_error.h"
26 struct status_thread_s {
27 assuan_context_t ctx;
28 gchar *status;
29 const gchar *line;
30 pth_t tid;
31 gint fd[2];
32 pth_event_t ev;
35 static void cleanup(void *arg)
37 struct status_thread_s *s = arg;
39 if (s->tid)
40 pth_cancel(s->tid);
42 close(s->fd[0]);
43 close(s->fd[1]);
44 pth_event_free(s->ev, PTH_FREE_ALL);
45 g_free(s);
48 static void *write_status_thread(void *arg)
50 struct status_thread_s *s = arg;
51 gpg_error_t rc;
52 pth_attr_t attr = pth_attr_of(pth_self());
54 pth_attr_set(attr, PTH_ATTR_NAME, __FUNCTION__);
55 pth_attr_destroy(attr);
56 pth_cancel_state(PTH_CANCEL_ASYNCHRONOUS, NULL);
57 rc = assuan_write_status(s->ctx, s->status, s->line);
58 pth_write(s->fd[1], &rc, sizeof(gpg_error_t));
59 return NULL;
62 gpg_error_t send_status(assuan_context_t ctx, status_msg_t which,
63 const gchar *fmt, ...)
65 const gchar *line = NULL;
66 gchar buf[ASSUAN_LINELENGTH];
67 gchar *status = NULL;
68 va_list ap;
69 gint n;
70 gpg_error_t rc;
71 struct status_thread_s *s;
72 gint to = get_key_file_integer("global", "keepalive");
73 pth_attr_t attr;
74 pth_event_t tev;
75 pth_status_t st;
77 if (fmt) {
78 va_start(ap, fmt);
79 g_vsnprintf(buf, sizeof(buf), fmt, ap);
80 va_end(ap);
81 line = buf;
84 switch (which) {
85 case STATUS_CACHE:
86 CACHE_LOCK(client->ctx);
87 line = print_fmt(buf, sizeof(buf), "%i", cache_file_count());
88 CACHE_UNLOCK;
89 status = "CACHE";
90 break;
91 case STATUS_CLIENTS:
92 MUTEX_LOCK(&cn_mutex);
93 line = print_fmt(buf, sizeof(buf), "%i", g_slist_length(cn_thread_list));
94 MUTEX_UNLOCK(&cn_mutex);
95 status = "CLIENTS";
96 break;
97 case STATUS_CONFIG:
98 status = "CONFIG";
99 break;
100 case STATUS_KEEPALIVE:
101 status = "KEEPALIVE";
102 break;
103 case STATUS_LOCKED:
104 status = "LOCKED";
105 line = N_("Waiting for lock");
106 break;
107 case STATUS_ENCRYPT:
108 status = "ENCRYPT";
109 break;
110 case STATUS_DECRYPT:
111 status = "DECRYPT";
112 break;
113 case STATUS_DECOMPRESS:
114 status = "DECOMPRESS";
115 break;
116 case STATUS_COMPRESS:
117 status = "COMPRESS";
118 break;
121 if (!ctx) {
122 log_write("%s %s", status, line);
123 return 0;
126 s = g_malloc0(sizeof(struct status_thread_s));
128 if (!s) {
129 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
130 return gpg_error_from_errno(ENOMEM);
133 s->ctx = ctx;
134 s->status = status;
135 s->line = line;
137 if (pipe(s->fd) == -1) {
138 n = errno;
139 g_free(s);
140 return gpg_error_from_errno(n);
143 pth_cleanup_push(cleanup, s);
144 /* Since we use the keepalive from the configuration, it may be 0. If so,
145 * status messages would fail and abort the connection. So use a default
146 * that doesn't affect the configured keepalive value. */
147 to = to <= 0 ? DEFAULT_KEEPALIVE_TO : to;
148 s->ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_FD_READABLE, s->fd[0]);
149 tev = pth_event(PTH_EVENT_TIME, pth_timeout(to, 0));
150 s->ev = pth_event_concat(s->ev, tev, NULL);
151 attr = pth_attr_new();
152 pth_attr_init(attr);
153 pth_attr_set(attr, PTH_ATTR_JOINABLE, FALSE);
154 s->tid = pth_spawn(attr, write_status_thread, s);
155 pth_yield(s->tid);
156 pth_attr_destroy(attr);
157 pth_wait(s->ev);
158 st = pth_event_status(s->ev);
160 if (st == PTH_STATUS_FAILED) {
161 pth_cancel(s->tid);
162 s->tid = NULL;
163 rc = GPG_ERR_ASS_WRITE_ERROR;
165 else if (st == PTH_STATUS_OCCURRED) {
166 size_t len = pth_read(s->fd[0], &rc, sizeof(gpg_error_t));
168 if (len != sizeof(gpg_error_t)) {
169 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(errno));
170 rc = GPG_ERR_ASS_WRITE_ERROR;
173 s->tid = NULL;
175 else {
176 st = pth_event_status(tev);
178 if (st == PTH_STATUS_OCCURRED) {
179 pth_cancel(s->tid);
180 s->tid = NULL;
181 rc = GPG_ERR_ASS_WRITE_ERROR;
185 pth_cleanup_pop(1);
186 return rc;
189 static void *do_send_status(void *arg)
191 struct client_s *cl = arg;
192 gpg_error_t rc;
194 rc = send_status(cl->ctx, cl->msg, NULL);
195 pth_exit((void *)rc);
196 return NULL;
199 void *client_msg_thread(void *arg)
201 struct client_thread_s *thd = arg;
202 pth_mutex_t m;
203 pth_attr_t attr = pth_attr_of(pth_self());
205 pth_attr_set(attr, PTH_ATTR_NAME, __FUNCTION__);
206 pth_attr_destroy(attr);
207 pth_mutex_init(&m);
208 pth_cond_init(&thd->msg_cond);
209 pth_mutex_acquire(&m, FALSE, NULL);
211 for (;;) {
212 pth_cond_await(&thd->msg_cond, &m, NULL);
213 pth_cancel_point();
215 for (;;) {
216 status_msg_t *msg;
217 pth_t tid;
218 gpg_error_t rc;
220 MUTEX_LOCK(&thd->msg_list_mutex);
221 msg = g_slist_nth_data(thd->msg_list, 0);
223 if (msg)
224 thd->msg_list = g_slist_remove(thd->msg_list, msg);
226 MUTEX_UNLOCK(&thd->msg_list_mutex);
228 if (!msg)
229 break;
231 thd->cl->msg = *msg;
232 g_free(msg);
233 tid = pth_spawn(NULL, do_send_status, thd->cl);
234 pth_yield(tid);
235 pth_join(tid, (void **)&rc);
236 pth_cancel_point();
238 if (rc) {
239 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(rc));
240 pth_cancel(thd->tid);
241 break;
246 return NULL;
249 static gboolean msg_list_dup(GSList *list, status_msg_t which)
251 guint i, t;
253 for (t = g_slist_length(list), i = 0; i < t; i++) {
254 status_msg_t *m = g_slist_nth_data(list, i);
256 if (*m == which)
257 return TRUE;
260 return FALSE;
263 void send_status_all(status_msg_t which)
265 guint i, t;
267 MUTEX_LOCK(&cn_mutex);
269 for (t = g_slist_length(cn_thread_list), i = 0; i < t; i++) {
270 struct client_thread_s *cn = g_slist_nth_data(cn_thread_list, i);
271 status_msg_t *m;
273 if (msg_list_dup(cn->msg_list, which))
274 continue;
276 m = g_malloc(sizeof(status_msg_t));
278 if (!m) {
279 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
280 continue;
283 *m = which;
284 MUTEX_LOCK(&cn->msg_list_mutex);
285 cn->msg_list = g_slist_append(cn->msg_list, m);
286 MUTEX_UNLOCK(&cn->msg_list_mutex);
287 pth_cond_notify(&cn->msg_cond, FALSE);
290 MUTEX_UNLOCK(&cn_mutex);