Require automake 2.60.
[pwmd.git] / src / status.c
blob64a1779ad7273dd3f41e7cf3b1707299f2850151
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 pth_event_t ev;
34 static void cleanup(void *arg)
36 struct status_thread_s *s = arg;
38 if (s->tid)
39 pth_cancel(s->tid);
41 pth_event_free(s->ev, PTH_FREE_ALL);
42 g_free(s);
45 static void *write_status_thread(void *arg)
47 struct status_thread_s *s = arg;
48 gpg_error_t rc;
49 pth_attr_t attr = pth_attr_of(pth_self());
51 pth_attr_set(attr, PTH_ATTR_NAME, __FUNCTION__);
52 pth_attr_destroy(attr);
53 pth_cancel_state(PTH_CANCEL_ASYNCHRONOUS, NULL);
54 rc = assuan_write_status(s->ctx, s->status, s->line);
55 pth_exit((void *)rc);
56 return NULL;
59 gpg_error_t send_status(assuan_context_t ctx, status_msg_t which,
60 const gchar *fmt, ...)
62 const gchar *line = NULL;
63 gchar buf[ASSUAN_LINELENGTH+1];
64 gchar *status = NULL;
65 va_list ap;
66 gint n;
67 gpg_error_t rc;
68 struct status_thread_s *s;
69 gint to = get_key_file_integer("global", "keepalive");
70 pth_attr_t attr;
71 pth_event_t tev;
72 pth_status_t st;
74 if (fmt) {
75 va_start(ap, fmt);
76 g_vsnprintf(buf, sizeof(buf), fmt, ap);
77 va_end(ap);
78 line = buf;
81 switch (which) {
82 case STATUS_XFER:
83 status = "XFER";
84 break;
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;
136 pth_cleanup_push(cleanup, s);
137 attr = pth_attr_new();
138 pth_attr_init(attr);
139 pth_attr_set(attr, PTH_ATTR_JOINABLE, TRUE);
140 s->tid = pth_spawn(attr, write_status_thread, s);
141 n = errno;
142 pth_attr_destroy(attr);
144 if (!s->tid) {
145 log_write("%s(%i): pth_spawn(): %s", __FILE__, __LINE__,
146 _gpg_strerror(gpg_error_from_errno(n)));
147 pth_cleanup_pop(1);
148 return gpg_error_from_errno(n);
151 /* Since we use the keepalive from the configuration, it may be 0. If so,
152 * status messages would fail and abort the connection. So use a default
153 * that doesn't affect the configured keepalive value. */
154 to = to <= 0 ? DEFAULT_KEEPALIVE_TO : to;
155 s->ev = pth_event(PTH_EVENT_TID|PTH_UNTIL_TID_DEAD, s->tid);
156 tev = pth_event(PTH_EVENT_TIME, pth_timeout(to, 0));
157 s->ev = pth_event_concat(s->ev, tev, NULL);
158 pth_yield(s->tid);
159 pth_wait(s->ev);
160 st = pth_event_status(s->ev);
162 if (st == PTH_STATUS_FAILED) {
163 pth_cancel(s->tid);
164 s->tid = NULL;
165 rc = GPG_ERR_ASS_WRITE_ERROR;
168 else if (st == PTH_STATUS_OCCURRED) {
169 pth_join(s->tid, (void **)&rc);
170 s->tid = NULL;
172 else {
173 st = pth_event_status(tev);
175 if (st == PTH_STATUS_OCCURRED) {
176 pth_cancel(s->tid);
177 s->tid = NULL;
178 rc = GPG_ERR_ASS_WRITE_ERROR;
182 pth_cleanup_pop(1);
183 return rc;
186 void *client_msg_thread(void *arg)
188 struct client_thread_s *thd = arg;
189 pth_mutex_t m;
190 pth_attr_t attr = pth_attr_of(pth_self());
192 pth_attr_set(attr, PTH_ATTR_NAME, __FUNCTION__);
193 pth_attr_destroy(attr);
194 pth_mutex_init(&m);
195 pth_cond_init(&thd->msg_cond);
196 pth_mutex_acquire(&m, FALSE, NULL);
198 for (;;) {
199 pth_cond_await(&thd->msg_cond, &m, NULL);
200 pth_cancel_point();
202 for (;;) {
203 status_msg_t *msg;
204 gpg_error_t rc;
206 MUTEX_LOCK(&thd->msg_list_mutex);
207 msg = g_slist_nth_data(thd->msg_list, 0);
209 if (msg)
210 thd->msg_list = g_slist_remove(thd->msg_list, msg);
212 MUTEX_UNLOCK(&thd->msg_list_mutex);
214 if (!msg)
215 break;
217 rc = send_status(thd->cl->ctx, *msg, NULL);
218 g_free(msg);
219 pth_cancel_point();
221 if (rc) {
222 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(rc));
223 pth_cancel(thd->tid);
224 break;
229 return NULL;
232 static gboolean msg_list_dup(GSList *list, status_msg_t which)
234 guint i, t;
236 for (t = g_slist_length(list), i = 0; i < t; i++) {
237 status_msg_t *m = g_slist_nth_data(list, i);
239 if (*m == which)
240 return TRUE;
243 return FALSE;
246 void send_status_all(status_msg_t which)
248 guint i, t;
250 MUTEX_LOCK(&cn_mutex);
252 for (t = g_slist_length(cn_thread_list), i = 0; i < t; i++) {
253 struct client_thread_s *cn = g_slist_nth_data(cn_thread_list, i);
254 status_msg_t *m;
256 if (msg_list_dup(cn->msg_list, which))
257 continue;
259 m = g_malloc(sizeof(status_msg_t));
261 if (!m) {
262 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
263 continue;
266 *m = which;
267 MUTEX_LOCK(&cn->msg_list_mutex);
268 cn->msg_list = g_slist_append(cn->msg_list, m);
269 MUTEX_UNLOCK(&cn->msg_list_mutex);
270 pth_cond_notify(&cn->msg_cond, FALSE);
273 MUTEX_UNLOCK(&cn_mutex);