Add STATUS_MODIFIED.
[pwmd.git] / src / status.c
blob092781547c91457483551abca5b02a40024a3096
1 /*
2 Copyright (C) 2006-2022 Ben Kibbey <bjk@luxsci.net>
4 This file is part of pwmd.
6 Pwmd is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License version 2 as
8 published by the Free Software Foundation.
10 Pwmd 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 Pwmd. If not, see <http://www.gnu.org/licenses/>.
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif
22 #include <pthread.h>
23 #include <stdarg.h>
24 #include <sys/socket.h>
25 #ifdef HAVE_SYS_IOCTL_H
26 #include <sys/ioctl.h>
27 #endif
28 #include <errno.h>
29 #include <poll.h>
31 #ifdef HAVE_LINUX_SOCKIOS_H
32 #include <linux/sockios.h>
33 #endif
35 #ifdef HAVE_SYS_FILIO_H
36 #include <sys/filio.h>
37 #endif
39 #include "pwmd-error.h"
40 #include "mutex.h"
41 #include "util-misc.h"
42 #include "common.h"
43 #include "util-string.h"
44 #include "status.h"
45 #include "cache.h"
46 #include "mem.h"
48 gpg_error_t
49 send_status (assuan_context_t ctx, status_msg_t which, const char *fmt, ...)
51 const char *line = NULL;
52 char buf[ASSUAN_LINELENGTH + 1];
53 const char *status = NULL;
54 #ifdef WITH_GNUTLS
55 struct client_s *client = ctx ? assuan_get_pointer (ctx) : NULL;
56 #endif
57 gpg_error_t rc = 0;
59 if (fmt)
61 va_list ap;
63 va_start (ap, fmt);
64 vsnprintf (buf, sizeof (buf), fmt, ap);
65 va_end (ap);
66 line = buf;
69 switch (which)
71 #ifdef WITH_GNUTLS
72 case STATUS_REHANDSHAKE:
73 status = "REHANDSHAKE";
74 break;
75 #endif
76 case STATUS_MODIFIED:
77 status = "MODIFIED";
78 break;
79 case STATUS_EXPIRE:
80 status = "EXPIRE";
81 break;
82 case STATUS_GENKEY:
83 status = "GENKEY";
84 break;
85 case STATUS_XFER:
86 status = "XFER";
87 break;
88 case STATUS_CACHE:
89 snprintf (buf, sizeof (buf), "%u", cache_file_count ());
90 line = buf;
91 status = "CACHE";
92 break;
93 case STATUS_CLIENTS:
94 MUTEX_LOCK (&cn_mutex);
95 snprintf (buf, sizeof (buf), "%u", slist_length (cn_thread_list));
96 line = buf;
97 MUTEX_UNLOCK (&cn_mutex);
98 status = "CLIENTS";
99 break;
100 case STATUS_LOCKED:
101 status = "LOCKED";
102 line = _("Waiting for lock");
103 break;
104 case STATUS_ENCRYPT:
105 status = "ENCRYPT";
106 break;
107 case STATUS_DECRYPT:
108 status = "DECRYPT";
109 break;
110 case STATUS_NEWFILE:
111 status = "NEWFILE";
112 break;
113 case STATUS_KEEPALIVE:
114 status = "KEEPALIVE";
115 break;
116 case STATUS_GPGME:
117 status = "GPGME";
118 break;
119 case STATUS_STATE:
120 status = "STATE";
121 break;
122 case STATUS_BULK:
123 status = "BULK";
126 if (!ctx)
128 log_write ("%s %s", status, line ? line : "");
129 return 0;
132 #ifdef WITH_GNUTLS
133 if (client && client->thd->remote && which == STATUS_KEEPALIVE)
135 int buffered = 0;
137 #ifdef HAVE_DECL_SIOCOUTQ
138 if (ioctl (client->thd->fd, SIOCOUTQ, &buffered) == -1)
139 log_write ("%s(%i): ioctl: %s", __FUNCTION__, __LINE__,
140 pwmd_strerror (gpg_error_from_errno (errno)));
141 #elif defined (HAVE_DECL_FIONWRITE)
142 if (ioctl (client->thd->fd, FIONWRITE, &buffered) == -1)
143 log_write ("%s(%i): ioctl: %s", __FUNCTION__, __LINE__,
144 pwmd_strerror (gpg_error_from_errno (errno)));
145 #elif !defined (HAVE_WINLIKE_SYSTEM)
146 if (1)
148 int sndbuf;
149 socklen_t len = sizeof(int);
151 if (getsockopt (client->thd->fd, SOL_SOCKET, SO_SNDBUF, &sndbuf,
152 &len) == -1)
153 log_write ("%s(%i): getsockopt: %s", __FUNCTION__, __LINE__,
154 pwmd_strerror (gpg_error_from_errno (errno)));
155 else
157 int lowat;
159 len = sizeof(int);
160 if (getsockopt (client->thd->fd, SOL_SOCKET, SO_SNDLOWAT,
161 &lowat, &len) == -1)
162 log_write ("%s(%i): getsockopt: %s", __FUNCTION__,
163 __LINE__, pwmd_strerror (gpg_error_from_errno (errno)));
164 else
166 len = sizeof(int);
167 if (setsockopt (client->thd->fd, SOL_SOCKET, SO_SNDLOWAT,
168 &sndbuf, len) == -1)
169 log_write ("%s(%i): setsockopt: %s", __FUNCTION__,
170 __LINE__,
171 pwmd_strerror (gpg_error_from_errno (errno)));
172 else
174 struct pollfd fds[1];
175 int n;
177 fds[0].fd = client->thd->fd;
178 fds[0].events = POLLOUT;
180 buffered = client->thd->last_buffer_size + 1;
181 n = poll (fds, 1, 0);
182 len = sizeof(int);
183 if (setsockopt (client->thd->fd, SOL_SOCKET, SO_SNDLOWAT,
184 &lowat, len) == -1)
185 log_write ("%s(%i): setsockopt: %s", __FUNCTION__,
186 __LINE__,
187 pwmd_strerror (gpg_error_from_errno (errno)));
188 if (n > 0 && (fds[0].revents & POLLOUT))
189 buffered = 0;
194 #endif
195 if (buffered)
197 int interval = config_get_integer ("global", "keepalive_interval");
198 int timeout = config_get_integer ("global", "tls_timeout");
200 if (buffered < client->thd->last_buffer_size)
201 client->thd->buffer_timeout = 0;
203 client->thd->last_buffer_size = buffered;
205 if (++client->thd->buffer_timeout * interval >= timeout)
206 rc = gpg_error (GPG_ERR_ETIMEDOUT);
208 else
209 client->thd->buffer_timeout = client->thd->last_buffer_size = 0;
211 #endif
213 if (!rc)
214 rc = assuan_write_status (ctx, status, line);
216 #ifdef WITH_GNUTLS
217 if (client && client->thd->remote && which != STATUS_KEEPALIVE)
218 client->thd->buffer_timeout = client->thd->last_buffer_size = 0;
219 #endif
221 return rc;
224 static void
225 do_send_status_all (struct slist_s *list, status_msg_t s,
226 const char *line, pthread_t *not_tid)
228 int i = 0;
229 int t = slist_length (list);
231 for (; i < t; i++)
233 struct client_thread_s *thd = slist_nth_data (list, i);
234 struct status_msg_s *msg, *p;
235 char c = 0xff;
236 int match = 0;
237 gpg_error_t rc;
239 if (not_tid && pthread_equal (*not_tid, thd->tid))
240 continue;
242 if (thd->state == CLIENT_STATE_UNKNOWN
243 || thd->state == CLIENT_STATE_DISCON)
244 continue;
246 MUTEX_LOCK (&thd->status_mutex);
247 if (s == STATUS_STATE && !thd->send_state)
249 MUTEX_UNLOCK (&thd->status_mutex);
250 continue;
253 pthread_cleanup_push (release_mutex_cb, &thd->status_mutex);
255 for (p = thd->msg_queue; p; p = p->next)
257 if (p->s == s)
259 match = 1;
260 break;
264 if (match && s != STATUS_STATE)
266 xfree (p->line);
267 p->line = line ? str_dup (line) : NULL;
269 if (!thd->wrote_status)
271 ssize_t ret = write (thd->status_msg_pipe[1], &c, 1);
273 rc = gpg_error_from_syserror ();
274 if (ret == -1)
275 log_write ("%s (%i): %s", __FUNCTION__, __LINE__,
276 pwmd_strerror (rc));
279 thd->wrote_status = 1;
281 else
283 msg = xcalloc (1, sizeof (struct status_msg_s));
284 msg->s = s;
285 msg->line = line ? str_dup (line) : NULL;
287 for (p = thd->msg_queue; p && p->next; p = p->next);
288 if (!p)
289 thd->msg_queue = msg;
290 else
291 p->next = msg;
293 if (!thd->wrote_status)
295 ssize_t ret = write (thd->status_msg_pipe[1], &c, 1);
297 rc = gpg_error_from_syserror ();
298 if (ret == -1)
299 log_write ("%s (%i): %s", __FUNCTION__, __LINE__,
300 pwmd_strerror (rc));
303 thd->wrote_status = 1;
306 pthread_cleanup_pop (1);
310 void
311 send_status_all_not_self (status_msg_t s, const char *fmt, ...)
313 MUTEX_LOCK (&cn_mutex);
314 char *line = NULL;
315 pthread_t tid = pthread_self ();
317 if (fmt)
319 va_list ap;
321 va_start (ap, fmt);
322 str_vasprintf (&line, fmt, ap);
323 va_end (ap);
326 pthread_cleanup_push (release_mutex_cb, &cn_mutex);
327 pthread_cleanup_push (xfree, line);
328 do_send_status_all (cn_thread_list, s, line, &tid);
329 pthread_cleanup_pop (1);
330 pthread_cleanup_pop (1);
333 void
334 send_status_all (status_msg_t s, const char *fmt, ...)
336 MUTEX_LOCK (&cn_mutex);
337 char *line = NULL;
339 if (fmt)
341 va_list ap;
343 va_start (ap, fmt);
344 str_vasprintf (&line, fmt, ap);
345 va_end (ap);
348 pthread_cleanup_push (release_mutex_cb, &cn_mutex);
349 pthread_cleanup_push (xfree, line);
350 do_send_status_all (cn_thread_list, s, line, NULL);
351 pthread_cleanup_pop (1);
352 pthread_cleanup_pop (1);
355 static void
356 free_status_list (void *arg)
358 struct slist_s *list = arg;
360 slist_free (list);
363 void
364 send_status_modified (assuan_context_t ctx)
366 MUTEX_LOCK (&cn_mutex);
367 int i = 0;
368 int t = slist_length (cn_thread_list);
369 struct slist_s *list = NULL;
370 gpg_error_t rc = 0;
371 struct client_s *client = assuan_get_pointer (ctx);
373 pthread_cleanup_push (release_mutex_cb, &cn_mutex);
375 for (; i < t; i++)
377 struct slist_s *p;
378 struct client_thread_s *thd = slist_nth_data (cn_thread_list, i);
380 if (pthread_equal (client->thd->tid, thd->tid))
381 continue;
383 if (thd->cl->filename && strcmp (thd->cl->filename, client->filename))
384 continue;
385 else if (!thd->cl->filename)
386 continue;
388 p = slist_append (list, thd);
389 if (!p)
391 slist_free (list);
392 rc = GPG_ERR_ENOMEM;
393 break;
396 list = p;
399 if (!rc && list)
401 char line[255];
403 snprintf (line, sizeof (line), "%p", &client->thd->tid);
404 pthread_cleanup_push (free_status_list, list);
405 do_send_status_all (list, STATUS_MODIFIED, line, NULL);
406 pthread_cleanup_pop (1);
409 pthread_cleanup_pop (1);