Update NEWS.
[pwmd.git] / src / status.c
blob618e8d8dace2fdd571fa11d21c7c00033b3925db
1 /*
2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015,
3 2016
4 Ben Kibbey <bjk@luxsci.net>
6 This file is part of pwmd.
8 Pwmd is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
11 (at your option) any later version.
13 Pwmd is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with Pwmd. If not, see <http://www.gnu.org/licenses/>.
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #include <pthread.h>
26 #include <stdarg.h>
27 #include <sys/socket.h>
28 #include <sys/ioctl.h>
29 #include <errno.h>
30 #include <poll.h>
32 #ifdef HAVE_LINUX_SOCKIOS_H
33 #include <linux/sockios.h>
34 #endif
36 #ifdef HAVE_SYS_FILIO_H
37 #include <sys/filio.h>
38 #endif
40 #include "pwmd-error.h"
41 #include "mutex.h"
42 #include "util-misc.h"
43 #include "common.h"
44 #include "util-string.h"
45 #include "status.h"
46 #include "cache.h"
47 #include "mem.h"
49 gpg_error_t
50 send_status (assuan_context_t ctx, status_msg_t which, const char *fmt, ...)
52 const char *line = NULL;
53 char buf[ASSUAN_LINELENGTH + 1];
54 const char *status = NULL;
55 #ifdef WITH_GNUTLS
56 struct client_s *client = ctx ? assuan_get_pointer (ctx) : NULL;
57 #endif
58 gpg_error_t rc = 0;
60 if (fmt)
62 va_list ap;
64 va_start (ap, fmt);
65 vsnprintf (buf, sizeof (buf), fmt, ap);
66 va_end (ap);
67 line = buf;
70 switch (which)
72 #ifdef WITH_GNUTLS
73 case STATUS_REHANDSHAKE:
74 status = "REHANDSHAKE";
75 break;
76 #endif
77 case STATUS_EXPIRE:
78 status = "EXPIRE";
79 break;
80 case STATUS_GENKEY:
81 status = "GENKEY";
82 break;
83 case STATUS_XFER:
84 status = "XFER";
85 break;
86 case STATUS_CACHE:
87 snprintf (buf, sizeof (buf), "%u", cache_file_count ());
88 line = buf;
89 status = "CACHE";
90 break;
91 case STATUS_CLIENTS:
92 MUTEX_LOCK (&cn_mutex);
93 snprintf (buf, sizeof (buf), "%u", slist_length (cn_thread_list));
94 line = buf;
95 MUTEX_UNLOCK (&cn_mutex);
96 status = "CLIENTS";
97 break;
98 case STATUS_LOCKED:
99 status = "LOCKED";
100 line = _("Waiting for lock");
101 break;
102 case STATUS_ENCRYPT:
103 status = "ENCRYPT";
104 break;
105 case STATUS_DECRYPT:
106 status = "DECRYPT";
107 break;
108 case STATUS_NEWFILE:
109 status = "NEWFILE";
110 break;
111 case STATUS_KEEPALIVE:
112 status = "KEEPALIVE";
113 break;
114 case STATUS_GPGME:
115 status = "GPGME";
116 break;
117 case STATUS_STATE:
118 status = "STATE";
119 break;
122 if (!ctx)
124 log_write ("%s %s", status, line ? line : "");
125 return 0;
128 #ifdef WITH_GNUTLS
129 if (client && client->thd->remote && which == STATUS_KEEPALIVE)
131 int buffered = 0;
133 #ifdef HAVE_DECL_SIOCOUTQ
134 if (ioctl (client->thd->fd, SIOCOUTQ, &buffered) == -1)
135 log_write ("%s(%i): ioctl: %s", __FUNCTION__, __LINE__,
136 pwmd_strerror (gpg_error_from_errno (errno)));
137 #elif defined (HAVE_DECL_FIONWRITE)
138 if (ioctl (client->thd->fd, FIONWRITE, &buffered) == -1)
139 log_write ("%s(%i): ioctl: %s", __FUNCTION__, __LINE__,
140 pwmd_strerror (gpg_error_from_errno (errno)));
141 #else
142 if (1)
144 int sndbuf;
145 socklen_t len = sizeof(int);
147 if (getsockopt (client->thd->fd, SOL_SOCKET, SO_SNDBUF, &sndbuf,
148 &len) == -1)
149 log_write ("%s(%i): getsockopt: %s", __FUNCTION__, __LINE__,
150 pwmd_strerror (gpg_error_from_errno (errno)));
151 else
153 int lowat;
155 len = sizeof(int);
156 if (getsockopt (client->thd->fd, SOL_SOCKET, SO_SNDLOWAT,
157 &lowat, &len) == -1)
158 log_write ("%s(%i): getsockopt: %s", __FUNCTION__,
159 __LINE__, pwmd_strerror (gpg_error_from_errno (errno)));
160 else
162 len = sizeof(int);
163 if (setsockopt (client->thd->fd, SOL_SOCKET, SO_SNDLOWAT,
164 &sndbuf, len) == -1)
165 log_write ("%s(%i): setsockopt: %s", __FUNCTION__,
166 __LINE__,
167 pwmd_strerror (gpg_error_from_errno (errno)));
168 else
170 struct pollfd fds[1];
171 int n;
173 fds[0].fd = client->thd->fd;
174 fds[0].events = POLLOUT;
176 buffered = client->thd->last_buffer_size + 1;
177 n = poll (fds, 1, 0);
178 len = sizeof(int);
179 if (setsockopt (client->thd->fd, SOL_SOCKET, SO_SNDLOWAT,
180 &lowat, len) == -1)
181 log_write ("%s(%i): setsockopt: %s", __FUNCTION__,
182 __LINE__,
183 pwmd_strerror (gpg_error_from_errno (errno)));
184 if (n > 0 && (fds[0].revents & POLLOUT))
185 buffered = 0;
190 #endif
191 if (buffered)
193 int interval = config_get_integer ("global", "keepalive_interval");
194 int timeout = config_get_integer ("global", "tls_timeout");
196 if (buffered < client->thd->last_buffer_size)
197 client->thd->buffer_timeout = 0;
199 client->thd->last_buffer_size = buffered;
201 if (++client->thd->buffer_timeout * interval >= timeout)
202 rc = gpg_error (GPG_ERR_ETIMEDOUT);
204 else
205 client->thd->buffer_timeout = client->thd->last_buffer_size = 0;
207 #endif
209 if (!rc)
210 rc = assuan_write_status (ctx, status, line);
212 #ifdef WITH_GNUTLS
213 if (client && client->thd->remote && which != STATUS_KEEPALIVE)
214 client->thd->buffer_timeout = client->thd->last_buffer_size = 0;
215 #endif
217 return rc;
220 static void
221 do_send_status_all (status_msg_t s, const char *line, pthread_t *not_tid)
223 MUTEX_LOCK (&cn_mutex);
224 int i = 0;
225 int t = slist_length (cn_thread_list);
227 pthread_cleanup_push (release_mutex_cb, &cn_mutex);
229 for (; i < t; i++)
231 struct client_thread_s *thd = slist_nth_data (cn_thread_list, i);
232 struct status_msg_s *msg, *p;
233 char c = 0xff;
234 int match = 0;
235 gpg_error_t rc;
237 if (not_tid && pthread_equal (*not_tid, thd->tid))
238 continue;
240 if (thd->state == CLIENT_STATE_UNKNOWN
241 || thd->state == CLIENT_STATE_DISCON)
242 continue;
243 else if (s == STATUS_STATE && !thd->cl->client_state)
244 continue;
246 MUTEX_LOCK (&thd->status_mutex);
247 pthread_cleanup_push (release_mutex_cb, &thd->status_mutex);
249 for (p = thd->msg_queue; p; p = p->next)
251 if (p->s == s)
253 match = 1;
254 break;
258 if (match && s != STATUS_STATE)
260 xfree (p->line);
261 p->line = line ? str_dup (line) : NULL;
263 if (!thd->wrote_status)
265 ssize_t ret = write (thd->status_msg_pipe[1], &c, 1);
267 rc = gpg_error_from_syserror ();
268 if (ret == -1)
269 log_write ("%s (%i): %s", __FUNCTION__, __LINE__,
270 pwmd_strerror (rc));
273 thd->wrote_status = 1;
275 else
277 msg = xcalloc (1, sizeof (struct status_msg_s));
278 msg->s = s;
279 msg->line = line ? str_dup (line) : NULL;
281 for (p = thd->msg_queue; p && p->next; p = p->next);
282 if (!p)
283 thd->msg_queue = msg;
284 else
285 p->next = msg;
287 if (!thd->wrote_status)
289 ssize_t ret = write (thd->status_msg_pipe[1], &c, 1);
291 rc = gpg_error_from_syserror ();
292 if (ret == -1)
293 log_write ("%s (%i): %s", __FUNCTION__, __LINE__,
294 pwmd_strerror (rc));
297 thd->wrote_status = 1;
300 pthread_cleanup_pop (1);
303 pthread_cleanup_pop (1);
306 void
307 send_status_all_not_self (status_msg_t s, const char *fmt, ...)
309 char *line = NULL;
310 pthread_t tid = pthread_self ();
312 if (fmt)
314 va_list ap;
316 va_start (ap, fmt);
317 str_vasprintf (&line, fmt, ap);
318 va_end (ap);
321 pthread_cleanup_push (xfree, line);
322 do_send_status_all (s, line, &tid);
323 pthread_cleanup_pop (1);
326 void
327 send_status_all (status_msg_t s, const char *fmt, ...)
329 char *line = NULL;
331 if (fmt)
333 va_list ap;
335 va_start (ap, fmt);
336 str_vasprintf (&line, fmt, ap);
337 va_end (ap);
340 pthread_cleanup_push (xfree, line);
341 do_send_status_all (s, line, NULL);
342 pthread_cleanup_pop (1);