1/* Copyright (c) 1998-2018 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library 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 GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <http://www.gnu.org/licenses/>. */
18
19#include <errno.h>
20#include <error.h>
21#include <inttypes.h>
22#include <langinfo.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sys/socket.h>
27#include <unistd.h>
28#include <libintl.h>
29
30#include "nscd.h"
31#include "dbg_log.h"
32#include "selinux.h"
33#ifdef HAVE_SELINUX
34# include <selinux/selinux.h>
35# include <selinux/avc.h>
36#endif /* HAVE_SELINUX */
37
38/* We use this to make sure the receiver is the same. The lower 16
39 bits are reserved for flags indicating compilation variants. This
40 version needs to be updated if the definition of struct statdata
41 changes. */
42#define STATDATA_VERSION 0x01020000U
43
44#ifdef HAVE_SELINUX
45# define STATDATA_VERSION_SELINUX_FLAG 0x0001U
46#else
47# define STATDATA_VERSION_SELINUX_FLAG 0x0000U
48#endif
49
50/* All flags affecting the struct statdata layout. */
51#define STATDATA_VERSION_FLAGS STATDATA_VERSION_SELINUX_FLAG
52
53/* The full version number for struct statdata. */
54#define STATDATA_VERSION_FULL (STATDATA_VERSION | STATDATA_VERSION_FLAGS)
55
56/* Statistic data for one database. */
57struct dbstat
58{
59 int enabled;
60 int check_file;
61 int shared;
62 int persistent;
63 size_t module;
64
65 unsigned long int postimeout;
66 unsigned long int negtimeout;
67
68 size_t nentries;
69 size_t maxnentries;
70 size_t maxnsearched;
71 size_t datasize;
72 size_t dataused;
73
74 uintmax_t poshit;
75 uintmax_t neghit;
76 uintmax_t posmiss;
77 uintmax_t negmiss;
78
79 uintmax_t rdlockdelayed;
80 uintmax_t wrlockdelayed;
81
82 uintmax_t addfailed;
83};
84
85/* Record for transmitting statistics. If this definition changes,
86 update STATDATA_VERSION above. */
87struct statdata
88{
89 unsigned int version; /* Must be STATDATA_VERSION_FULL. */
90 int debug_level;
91 time_t runtime;
92 unsigned long int client_queued;
93 int nthreads;
94 int max_nthreads;
95 int paranoia;
96 time_t restart_interval;
97 unsigned int reload_count;
98 int ndbs;
99 struct dbstat dbs[lastdb];
100#ifdef HAVE_SELINUX
101 struct avc_cache_stats cstats;
102#endif /* HAVE_SELINUX */
103};
104
105
106void
107send_stats (int fd, struct database_dyn dbs[lastdb])
108{
109 struct statdata data;
110 int cnt;
111
112 memset (&data, 0, sizeof (data));
113
114 data.version = STATDATA_VERSION_FULL;
115 data.debug_level = debug_level;
116 data.runtime = time (NULL) - start_time;
117 data.client_queued = client_queued;
118 data.nthreads = nthreads;
119 data.max_nthreads = max_nthreads;
120 data.paranoia = paranoia;
121 data.restart_interval = restart_interval;
122 data.reload_count = reload_count;
123 data.ndbs = lastdb;
124
125 for (cnt = 0; cnt < lastdb; ++cnt)
126 {
127 memset (&data.dbs[cnt], 0, sizeof (data.dbs[cnt]));
128 data.dbs[cnt].enabled = dbs[cnt].enabled;
129 data.dbs[cnt].check_file = dbs[cnt].check_file;
130 data.dbs[cnt].shared = dbs[cnt].shared;
131 data.dbs[cnt].persistent = dbs[cnt].persistent;
132 data.dbs[cnt].postimeout = dbs[cnt].postimeout;
133 data.dbs[cnt].negtimeout = dbs[cnt].negtimeout;
134 if (dbs[cnt].head != NULL)
135 {
136 data.dbs[cnt].module = dbs[cnt].head->module;
137 data.dbs[cnt].poshit = dbs[cnt].head->poshit;
138 data.dbs[cnt].neghit = dbs[cnt].head->neghit;
139 data.dbs[cnt].posmiss = dbs[cnt].head->posmiss;
140 data.dbs[cnt].negmiss = dbs[cnt].head->negmiss;
141 data.dbs[cnt].nentries = dbs[cnt].head->nentries;
142 data.dbs[cnt].maxnentries = dbs[cnt].head->maxnentries;
143 data.dbs[cnt].datasize = dbs[cnt].head->data_size;
144 data.dbs[cnt].dataused = dbs[cnt].head->first_free;
145 data.dbs[cnt].maxnsearched = dbs[cnt].head->maxnsearched;
146 data.dbs[cnt].rdlockdelayed = dbs[cnt].head->rdlockdelayed;
147 data.dbs[cnt].wrlockdelayed = dbs[cnt].head->wrlockdelayed;
148 data.dbs[cnt].addfailed = dbs[cnt].head->addfailed;
149 }
150 }
151
152 if (selinux_enabled)
153 nscd_avc_cache_stats (&data.cstats);
154
155 if (TEMP_FAILURE_RETRY (send (fd, &data, sizeof (data), MSG_NOSIGNAL))
156 != sizeof (data))
157 {
158 char buf[256];
159 dbg_log (_("cannot write statistics: %s"),
160 strerror_r (errno, buf, sizeof (buf)));
161 }
162}
163
164
165int
166receive_print_stats (void)
167{
168 struct statdata data;
169 request_header req;
170 ssize_t nbytes;
171 int fd;
172 int i;
173 uid_t uid = getuid ();
174 const char *yesstr = _("yes");
175 const char *nostr = _("no");
176
177 /* Find out whether there is another user but root allowed to
178 request statistics. */
179 if (uid != 0)
180 {
181 /* User specified? */
182 if(stat_user == NULL || stat_uid != uid)
183 {
184 if (stat_user != NULL)
185 error (EXIT_FAILURE, 0,
186 _("Only root or %s is allowed to use this option!"),
187 stat_user);
188 else
189 error (EXIT_FAILURE, 0,
190 _("Only root is allowed to use this option!"));
191 }
192 }
193
194 /* Open a socket to the running nscd. */
195 fd = nscd_open_socket ();
196 if (fd == -1)
197 error (EXIT_FAILURE, 0, _("nscd not running!\n"));
198
199 /* Send the request. */
200 req.version = NSCD_VERSION;
201 req.type = GETSTAT;
202 req.key_len = 0;
203 nbytes = TEMP_FAILURE_RETRY (send (fd, &req, sizeof (request_header),
204 MSG_NOSIGNAL));
205 if (nbytes != sizeof (request_header))
206 {
207 int err = errno;
208 close (fd);
209 error (EXIT_FAILURE, err, _("write incomplete"));
210 }
211
212 /* Read as much data as we expect. */
213 if (TEMP_FAILURE_RETRY (read (fd, &data, sizeof (data))) != sizeof (data)
214 || (data.version != STATDATA_VERSION_FULL
215 /* Yes, this is an assignment! */
216 && (errno = EINVAL)))
217 {
218 /* Not the right version. */
219 int err = errno;
220 close (fd);
221 error (EXIT_FAILURE, err, _("cannot read statistics data"));
222 }
223
224 printf (_("nscd configuration:\n\n%15d server debug level\n"),
225 data.debug_level);
226
227 /* We know that we can simply subtract time_t values. */
228 unsigned long int diff = data.runtime;
229 unsigned int ndays = 0;
230 unsigned int nhours = 0;
231 unsigned int nmins = 0;
232 if (diff > 24 * 60 * 60)
233 {
234 ndays = diff / (24 * 60 * 60);
235 diff %= 24 * 60 * 60;
236 }
237 if (diff > 60 * 60)
238 {
239 nhours = diff / (60 * 60);
240 diff %= 60 * 60;
241 }
242 if (diff > 60)
243 {
244 nmins = diff / 60;
245 diff %= 60;
246 }
247 if (ndays != 0)
248 printf (_("%3ud %2uh %2um %2lus server runtime\n"),
249 ndays, nhours, nmins, diff);
250 else if (nhours != 0)
251 printf (_(" %2uh %2um %2lus server runtime\n"), nhours, nmins, diff);
252 else if (nmins != 0)
253 printf (_(" %2um %2lus server runtime\n"), nmins, diff);
254 else
255 printf (_(" %2lus server runtime\n"), diff);
256
257 printf (_("%15d current number of threads\n"
258 "%15d maximum number of threads\n"
259 "%15lu number of times clients had to wait\n"
260 "%15s paranoia mode enabled\n"
261 "%15lu restart internal\n"
262 "%15u reload count\n"),
263 data.nthreads, data.max_nthreads, data.client_queued,
264 data.paranoia ? yesstr : nostr,
265 (unsigned long int) data.restart_interval, data.reload_count);
266
267 for (i = 0; i < lastdb; ++i)
268 {
269 unsigned long int hit = data.dbs[i].poshit + data.dbs[i].neghit;
270 unsigned long int all = hit + data.dbs[i].posmiss + data.dbs[i].negmiss;
271 const char *enabled = data.dbs[i].enabled ? yesstr : nostr;
272 const char *check_file = data.dbs[i].check_file ? yesstr : nostr;
273 const char *shared = data.dbs[i].shared ? yesstr : nostr;
274 const char *persistent = data.dbs[i].persistent ? yesstr : nostr;
275
276 if (enabled[0] == '\0')
277 /* The locale does not provide this information so we have to
278 translate it ourself. Since we should avoid short translation
279 terms we artifically increase the length. */
280 enabled = data.dbs[i].enabled ? yesstr : nostr;
281 if (check_file[0] == '\0')
282 check_file = data.dbs[i].check_file ? yesstr : nostr;
283 if (shared[0] == '\0')
284 shared = data.dbs[i].shared ? yesstr : nostr;
285 if (persistent[0] == '\0')
286 persistent = data.dbs[i].persistent ? yesstr : nostr;
287
288 if (all == 0)
289 /* If nothing happened so far report a 0% hit rate. */
290 all = 1;
291
292 printf (_("\n%s cache:\n\n"
293 "%15s cache is enabled\n"
294 "%15s cache is persistent\n"
295 "%15s cache is shared\n"
296 "%15zu suggested size\n"
297 "%15zu total data pool size\n"
298 "%15zu used data pool size\n"
299 "%15lu seconds time to live for positive entries\n"
300 "%15lu seconds time to live for negative entries\n"
301 "%15" PRIuMAX " cache hits on positive entries\n"
302 "%15" PRIuMAX " cache hits on negative entries\n"
303 "%15" PRIuMAX " cache misses on positive entries\n"
304 "%15" PRIuMAX " cache misses on negative entries\n"
305 "%15lu%% cache hit rate\n"
306 "%15zu current number of cached values\n"
307 "%15zu maximum number of cached values\n"
308 "%15zu maximum chain length searched\n"
309 "%15" PRIuMAX " number of delays on rdlock\n"
310 "%15" PRIuMAX " number of delays on wrlock\n"
311 "%15" PRIuMAX " memory allocations failed\n"
312 "%15s check /etc/%s for changes\n"),
313 dbnames[i], enabled, persistent, shared,
314 data.dbs[i].module,
315 data.dbs[i].datasize, data.dbs[i].dataused,
316 data.dbs[i].postimeout, data.dbs[i].negtimeout,
317 data.dbs[i].poshit, data.dbs[i].neghit,
318 data.dbs[i].posmiss, data.dbs[i].negmiss,
319 (100 * hit) / all,
320 data.dbs[i].nentries, data.dbs[i].maxnentries,
321 data.dbs[i].maxnsearched,
322 data.dbs[i].rdlockdelayed,
323 data.dbs[i].wrlockdelayed,
324 data.dbs[i].addfailed, check_file, dbnames[i]);
325 }
326
327 if (selinux_enabled)
328 nscd_avc_print_stats (&data.cstats);
329
330 close (fd);
331
332 exit (0);
333}
334