Update copyright.
[pwmd.git] / src / status.c
blob686d31104450108e9ae165c615732ba7c3558fdb
1 /*
2 Copyright (C) 2006-2018 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 as published by
8 the Free Software Foundation, either version 2 of the License, or
9 (at your option) any later version.
11 Pwmd is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with Pwmd. If not, see <http://www.gnu.org/licenses/>.
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include <pthread.h>
24 #include <stdarg.h>
25 #include <sys/socket.h>
26 #include <sys/ioctl.h>
27 #include <errno.h>
28 #include <poll.h>
30 #ifdef HAVE_LINUX_SOCKIOS_H
31 #include <linux/sockios.h>
32 #endif
34 #ifdef HAVE_SYS_FILIO_H
35 #include <sys/filio.h>
36 #endif
38 #include "pwmd-error.h"
39 #include "mutex.h"
40 #include "util-misc.h"
41 #include "common.h"
42 #include "util-string.h"
43 #include "status.h"
44 #include "cache.h"
45 #include "mem.h"
47 gpg_error_t
48 send_status (assuan_context_t ctx, status_msg_t which, const char *fmt, ...)
50 const char *line = NULL;
51 char buf[ASSUAN_LINELENGTH + 1];
52 const char *status = NULL;
53 #ifdef WITH_GNUTLS
54 struct client_s *client = ctx ? assuan_get_pointer (ctx) : NULL;
55 #endif
56 gpg_error_t rc = 0;
58 if (fmt)
60 va_list ap;
62 va_start (ap, fmt);
63 vsnprintf (buf, sizeof (buf), fmt, ap);
64 va_end (ap);
65 line = buf;
68 switch (which)
70 #ifdef WITH_GNUTLS
71 case STATUS_REHANDSHAKE:
72 status = "REHANDSHAKE";
73 break;
74 #endif
75 case STATUS_EXPIRE:
76 status = "EXPIRE";
77 break;
78 case STATUS_GENKEY:
79 status = "GENKEY";
80 break;
81 case STATUS_XFER:
82 status = "XFER";
83 break;
84 case STATUS_CACHE:
85 snprintf (buf, sizeof (buf), "%u", cache_file_count ());
86 line = buf;
87 status = "CACHE";
88 break;
89 case STATUS_CLIENTS:
90 MUTEX_LOCK (&cn_mutex);
91 snprintf (buf, sizeof (buf), "%u", slist_length (cn_thread_list));
92 line = buf;
93 MUTEX_UNLOCK (&cn_mutex);
94 status = "CLIENTS";
95 break;
96 case STATUS_LOCKED:
97 status = "LOCKED";
98 line = _("Waiting for lock");
99 break;
100 case STATUS_ENCRYPT:
101 status = "ENCRYPT";
102 break;
103 case STATUS_DECRYPT:
104 status = "DECRYPT";
105 break;
106 case STATUS_NEWFILE:
107 status = "NEWFILE";
108 break;
109 case STATUS_KEEPALIVE:
110 status = "KEEPALIVE";
111 break;
112 case STATUS_GPGME:
113 status = "GPGME";
114 break;
115 case STATUS_STATE:
116 status = "STATE";
117 break;
120 if (!ctx)
122 log_write ("%s %s", status, line ? line : "");
123 return 0;
126 #ifdef WITH_GNUTLS
127 if (client && client->thd->remote && which == STATUS_KEEPALIVE)
129 int buffered = 0;
131 #ifdef HAVE_DECL_SIOCOUTQ
132 if (ioctl (client->thd->fd, SIOCOUTQ, &buffered) == -1)
133 log_write ("%s(%i): ioctl: %s", __FUNCTION__, __LINE__,
134 pwmd_strerror (gpg_error_from_errno (errno)));
135 #elif defined (HAVE_DECL_FIONWRITE)
136 if (ioctl (client->thd->fd, FIONWRITE, &buffered) == -1)
137 log_write ("%s(%i): ioctl: %s", __FUNCTION__, __LINE__,
138 pwmd_strerror (gpg_error_from_errno (errno)));
139 #elif !defined (HAVE_WINLIKE_SYSTEM)
140 if (1)
142 int sndbuf;
143 socklen_t len = sizeof(int);
145 if (getsockopt (client->thd->fd, SOL_SOCKET, SO_SNDBUF, &sndbuf,
146 &len) == -1)
147 log_write ("%s(%i): getsockopt: %s", __FUNCTION__, __LINE__,
148 pwmd_strerror (gpg_error_from_errno (errno)));
149 else
151 int lowat;
153 len = sizeof(int);
154 if (getsockopt (client->thd->fd, SOL_SOCKET, SO_SNDLOWAT,
155 &lowat, &len) == -1)
156 log_write ("%s(%i): getsockopt: %s", __FUNCTION__,
157 __LINE__, pwmd_strerror (gpg_error_from_errno (errno)));
158 else
160 len = sizeof(int);
161 if (setsockopt (client->thd->fd, SOL_SOCKET, SO_SNDLOWAT,
162 &sndbuf, len) == -1)
163 log_write ("%s(%i): setsockopt: %s", __FUNCTION__,
164 __LINE__,
165 pwmd_strerror (gpg_error_from_errno (errno)));
166 else
168 struct pollfd fds[1];
169 int n;
171 fds[0].fd = client->thd->fd;
172 fds[0].events = POLLOUT;
174 buffered = client->thd->last_buffer_size + 1;
175 n = poll (fds, 1, 0);
176 len = sizeof(int);
177 if (setsockopt (client->thd->fd, SOL_SOCKET, SO_SNDLOWAT,
178 &lowat, len) == -1)
179 log_write ("%s(%i): setsockopt: %s", __FUNCTION__,
180 __LINE__,
181 pwmd_strerror (gpg_error_from_errno (errno)));
182 if (n > 0 && (fds[0].revents & POLLOUT))
183 buffered = 0;
188 #endif
189 if (buffered)
191 int interval = config_get_integer ("global", "keepalive_interval");
192 int timeout = config_get_integer ("global", "tls_timeout");
194 if (buffered < client->thd->last_buffer_size)
195 client->thd->buffer_timeout = 0;
197 client->thd->last_buffer_size = buffered;
199 if (++client->thd->buffer_timeout * interval >= timeout)
200 rc = gpg_error (GPG_ERR_ETIMEDOUT);
202 else
203 client->thd->buffer_timeout = client->thd->last_buffer_size = 0;
205 #endif
207 if (!rc)
208 rc = assuan_write_status (ctx, status, line);
210 #ifdef WITH_GNUTLS
211 if (client && client->thd->remote && which != STATUS_KEEPALIVE)
212 client->thd->buffer_timeout = client->thd->last_buffer_size = 0;
213 #endif
215 return rc;
218 static void
219 do_send_status_all (status_msg_t s, const char *line, pthread_t *not_tid)
221 MUTEX_LOCK (&cn_mutex);
222 int i = 0;
223 int t = slist_length (cn_thread_list);
225 pthread_cleanup_push (release_mutex_cb, &cn_mutex);
227 for (; i < t; i++)
229 struct client_thread_s *thd = slist_nth_data (cn_thread_list, i);
230 struct status_msg_s *msg, *p;
231 char c = 0xff;
232 int match = 0;
233 gpg_error_t rc;
235 if (not_tid && pthread_equal (*not_tid, thd->tid))
236 continue;
238 if (thd->state == CLIENT_STATE_UNKNOWN
239 || thd->state == CLIENT_STATE_DISCON)
240 continue;
242 MUTEX_LOCK (&thd->status_mutex);
243 if (s == STATUS_STATE && !thd->cl->client_state)
245 MUTEX_UNLOCK (&thd->status_mutex);
246 continue;
249 pthread_cleanup_push (release_mutex_cb, &thd->status_mutex);
251 for (p = thd->msg_queue; p; p = p->next)
253 if (p->s == s)
255 match = 1;
256 break;
260 if (match && s != STATUS_STATE)
262 xfree (p->line);
263 p->line = line ? str_dup (line) : NULL;
265 if (!thd->wrote_status)
267 ssize_t ret = write (thd->status_msg_pipe[1], &c, 1);
269 rc = gpg_error_from_syserror ();
270 if (ret == -1)
271 log_write ("%s (%i): %s", __FUNCTION__, __LINE__,
272 pwmd_strerror (rc));
275 thd->wrote_status = 1;
277 else
279 msg = xcalloc (1, sizeof (struct status_msg_s));
280 msg->s = s;
281 msg->line = line ? str_dup (line) : NULL;
283 for (p = thd->msg_queue; p && p->next; p = p->next);
284 if (!p)
285 thd->msg_queue = msg;
286 else
287 p->next = msg;
289 if (!thd->wrote_status)
291 ssize_t ret = write (thd->status_msg_pipe[1], &c, 1);
293 rc = gpg_error_from_syserror ();
294 if (ret == -1)
295 log_write ("%s (%i): %s", __FUNCTION__, __LINE__,
296 pwmd_strerror (rc));
299 thd->wrote_status = 1;
302 pthread_cleanup_pop (1);
305 pthread_cleanup_pop (1);
308 void
309 send_status_all_not_self (status_msg_t s, const char *fmt, ...)
311 char *line = NULL;
312 pthread_t tid = pthread_self ();
314 if (fmt)
316 va_list ap;
318 va_start (ap, fmt);
319 str_vasprintf (&line, fmt, ap);
320 va_end (ap);
323 pthread_cleanup_push (xfree, line);
324 do_send_status_all (s, line, &tid);
325 pthread_cleanup_pop (1);
328 void
329 send_status_all (status_msg_t s, const char *fmt, ...)
331 char *line = NULL;
333 if (fmt)
335 va_list ap;
337 va_start (ap, fmt);
338 str_vasprintf (&line, fmt, ap);
339 va_end (ap);
342 pthread_cleanup_push (xfree, line);
343 do_send_status_all (s, line, NULL);
344 pthread_cleanup_pop (1);