1 | /* Copyright (c) 1998-2024 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. |
3 | |
4 | The GNU C Library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Lesser General Public |
6 | License as published by the Free Software Foundation; either |
7 | version 2.1 of the License, or (at your option) any later version. |
8 | |
9 | The GNU C Library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | Lesser General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Lesser General Public |
15 | License along with the GNU C Library; if not, see |
16 | <https://www.gnu.org/licenses/>. */ |
17 | |
18 | /* This file defines everything that client code should need to |
19 | know to talk to the nscd daemon. */ |
20 | |
21 | #ifndef _NSCD_CLIENT_H |
22 | #define _NSCD_CLIENT_H 1 |
23 | |
24 | #include <stdbool.h> |
25 | #include <stdint.h> |
26 | #include <string.h> |
27 | #include <time.h> |
28 | #include <sys/types.h> |
29 | #include <atomic.h> |
30 | #include <nscd-types.h> |
31 | #include <sys/uio.h> |
32 | |
33 | |
34 | /* Version number of the daemon interface */ |
35 | #define NSCD_VERSION 2 |
36 | |
37 | /* Path of the file where the PID of the running system is stored. */ |
38 | #define _PATH_NSCDPID "/var/run/nscd/nscd.pid" |
39 | |
40 | /* Path for the Unix domain socket. */ |
41 | #define _PATH_NSCDSOCKET "/var/run/nscd/socket" |
42 | |
43 | /* Path for the configuration file. */ |
44 | #define _PATH_NSCDCONF "/etc/nscd.conf" |
45 | |
46 | /* Maximum allowed length for the key. */ |
47 | #define MAXKEYLEN 1024 |
48 | |
49 | |
50 | /* Available services. */ |
51 | typedef enum |
52 | { |
53 | GETPWBYNAME, |
54 | GETPWBYUID, |
55 | GETGRBYNAME, |
56 | GETGRBYGID, |
57 | GETHOSTBYNAME, |
58 | GETHOSTBYNAMEv6, |
59 | GETHOSTBYADDR, |
60 | GETHOSTBYADDRv6, |
61 | SHUTDOWN, /* Shut the server down. */ |
62 | GETSTAT, /* Get the server statistic. */ |
63 | INVALIDATE, /* Invalidate one special cache. */ |
64 | GETFDPW, |
65 | GETFDGR, |
66 | GETFDHST, |
67 | GETAI, |
68 | INITGROUPS, |
69 | GETSERVBYNAME, |
70 | GETSERVBYPORT, |
71 | GETFDSERV, |
72 | GETNETGRENT, |
73 | INNETGR, |
74 | GETFDNETGR, |
75 | LASTREQ |
76 | } request_type; |
77 | |
78 | |
79 | /* Header common to all requests */ |
80 | typedef struct |
81 | { |
82 | int32_t ; /* Version number of the daemon interface. */ |
83 | request_type ; /* Service requested. */ |
84 | int32_t ; /* Key length. */ |
85 | } ; |
86 | |
87 | |
88 | /* Structure sent in reply to password query. Note that this struct is |
89 | sent also if the service is disabled or there is no record found. */ |
90 | typedef struct |
91 | { |
92 | int32_t ; |
93 | int32_t ; |
94 | nscd_ssize_t ; |
95 | nscd_ssize_t ; |
96 | uid_t ; |
97 | gid_t ; |
98 | nscd_ssize_t ; |
99 | nscd_ssize_t ; |
100 | nscd_ssize_t ; |
101 | } ; |
102 | |
103 | |
104 | /* Structure sent in reply to group query. Note that this struct is |
105 | sent also if the service is disabled or there is no record found. */ |
106 | typedef struct |
107 | { |
108 | int32_t ; |
109 | int32_t ; |
110 | nscd_ssize_t ; |
111 | nscd_ssize_t ; |
112 | gid_t ; |
113 | nscd_ssize_t ; |
114 | } ; |
115 | |
116 | |
117 | /* Structure sent in reply to host query. Note that this struct is |
118 | sent also if the service is disabled or there is no record found. */ |
119 | typedef struct |
120 | { |
121 | int32_t ; |
122 | int32_t ; |
123 | nscd_ssize_t ; |
124 | nscd_ssize_t ; |
125 | int32_t ; |
126 | int32_t ; |
127 | nscd_ssize_t ; |
128 | int32_t ; |
129 | } ; |
130 | |
131 | |
132 | /* Structure sent in reply to addrinfo query. Note that this struct is |
133 | sent also if the service is disabled or there is no record found. */ |
134 | typedef struct |
135 | { |
136 | int32_t ; |
137 | int32_t ; |
138 | nscd_ssize_t ; |
139 | nscd_ssize_t ; |
140 | nscd_ssize_t ; |
141 | int32_t ; |
142 | } ; |
143 | |
144 | /* Structure filled in by __nscd_getai. */ |
145 | struct nscd_ai_result |
146 | { |
147 | int naddrs; |
148 | char *canon; |
149 | uint8_t *family; |
150 | char *addrs; |
151 | }; |
152 | |
153 | /* Structure sent in reply to initgroups query. Note that this struct is |
154 | sent also if the service is disabled or there is no record found. */ |
155 | typedef struct |
156 | { |
157 | int32_t ; |
158 | int32_t ; |
159 | nscd_ssize_t ; |
160 | } ; |
161 | |
162 | |
163 | /* Structure sent in reply to services query. Note that this struct is |
164 | sent also if the service is disabled or there is no record found. */ |
165 | typedef struct |
166 | { |
167 | int32_t ; |
168 | int32_t ; |
169 | nscd_ssize_t ; |
170 | nscd_ssize_t ; |
171 | nscd_ssize_t ; |
172 | int32_t ; |
173 | } ; |
174 | |
175 | |
176 | /* Structure send in reply to netgroup query. Note that this struct is |
177 | sent also if the service is disabled or there is no record found. */ |
178 | typedef struct |
179 | { |
180 | int32_t ; |
181 | int32_t ; |
182 | nscd_ssize_t ; |
183 | nscd_ssize_t ; |
184 | } ; |
185 | |
186 | typedef struct |
187 | { |
188 | int32_t ; |
189 | int32_t ; |
190 | int32_t ; |
191 | } ; |
192 | |
193 | |
194 | /* Type for offsets in data part of database. */ |
195 | typedef uint32_t ref_t; |
196 | /* Value for invalid/no reference. */ |
197 | #define ENDREF UINT32_MAX |
198 | |
199 | /* Timestamp type. */ |
200 | typedef uint64_t nscd_time_t; |
201 | |
202 | /* Maximum timestamp. */ |
203 | #define MAX_TIMEOUT_VALUE \ |
204 | (sizeof (time_t) == sizeof (long int) ? LONG_MAX : INT_MAX) |
205 | |
206 | /* Alignment requirement of the beginning of the data region. */ |
207 | #define ALIGN 16 |
208 | |
209 | |
210 | /* Head of record in data part of database. */ |
211 | struct datahead |
212 | { |
213 | nscd_ssize_t allocsize; /* Allocated Bytes. */ |
214 | nscd_ssize_t recsize; /* Size of the record. */ |
215 | nscd_time_t timeout; /* Time when this entry becomes invalid. */ |
216 | uint8_t notfound; /* Nonzero if data has not been found. */ |
217 | uint8_t nreloads; /* Reloads without use. */ |
218 | uint8_t usable; /* False if the entry must be ignored. */ |
219 | uint8_t unused; /* Unused. */ |
220 | uint32_t ttl; /* TTL value used. */ |
221 | |
222 | /* We need to have the following element aligned for the response |
223 | header data types and their use in the 'struct dataset' types |
224 | defined in the XXXcache.c files. */ |
225 | union |
226 | { |
227 | pw_response_header pwdata; |
228 | gr_response_header grdata; |
229 | hst_response_header hstdata; |
230 | ai_response_header aidata; |
231 | initgr_response_header initgrdata; |
232 | serv_response_header servdata; |
233 | netgroup_response_header netgroupdata; |
234 | innetgroup_response_header innetgroupdata; |
235 | nscd_ssize_t align1; |
236 | nscd_time_t align2; |
237 | } data[0]; |
238 | }; |
239 | |
240 | static inline time_t |
241 | datahead_init_common (struct datahead *head, nscd_ssize_t allocsize, |
242 | nscd_ssize_t recsize, uint32_t ttl) |
243 | { |
244 | /* Initialize so that we don't write out junk in uninitialized data to the |
245 | cache. */ |
246 | memset (s: head, c: 0, n: sizeof (*head)); |
247 | |
248 | head->allocsize = allocsize; |
249 | head->recsize = recsize; |
250 | head->usable = true; |
251 | |
252 | head->ttl = ttl; |
253 | |
254 | /* Compute and return the timeout time. */ |
255 | return head->timeout = time (NULL) + ttl; |
256 | } |
257 | |
258 | static inline time_t |
259 | datahead_init_pos (struct datahead *head, nscd_ssize_t allocsize, |
260 | nscd_ssize_t recsize, uint8_t nreloads, uint32_t ttl) |
261 | { |
262 | time_t ret = datahead_init_common (head, allocsize, recsize, ttl); |
263 | |
264 | head->notfound = false; |
265 | head->nreloads = nreloads; |
266 | |
267 | return ret; |
268 | } |
269 | |
270 | static inline time_t |
271 | datahead_init_neg (struct datahead *head, nscd_ssize_t allocsize, |
272 | nscd_ssize_t recsize, uint32_t ttl) |
273 | { |
274 | time_t ret = datahead_init_common (head, allocsize, recsize, ttl); |
275 | |
276 | /* We don't need to touch nreloads here since it is set to our desired value |
277 | (0) when we clear the structure. */ |
278 | head->notfound = true; |
279 | |
280 | return ret; |
281 | } |
282 | |
283 | /* Structure for one hash table entry. */ |
284 | struct hashentry |
285 | { |
286 | request_type type:8; /* Which type of dataset. */ |
287 | bool first; /* True if this was the original key. */ |
288 | nscd_ssize_t len; /* Length of key. */ |
289 | ref_t key; /* Pointer to key. */ |
290 | int32_t owner; /* If secure table, this is the owner. */ |
291 | ref_t next; /* Next entry in this hash bucket list. */ |
292 | ref_t packet; /* Records for the result. */ |
293 | union |
294 | { |
295 | struct hashentry *dellist; /* Next record to be deleted. This can be a |
296 | pointer since only nscd uses this field. */ |
297 | ref_t *prevp; /* Pointer to field containing forward |
298 | reference. */ |
299 | }; |
300 | }; |
301 | |
302 | |
303 | /* Current persistent database version. */ |
304 | #define DB_VERSION 2 |
305 | |
306 | /* Maximum time allowed between updates of the timestamp. */ |
307 | #define MAPPING_TIMEOUT (5 * 60) |
308 | |
309 | |
310 | /* Used indices for the EXTRA_DATA element of 'database_pers_head'. |
311 | Each database has its own indices. */ |
312 | #define NSCD_HST_IDX_CONF_TIMESTAMP 0 |
313 | |
314 | |
315 | /* Header of persistent database file. */ |
316 | struct database_pers_head |
317 | { |
318 | int32_t version; |
319 | int32_t ; |
320 | volatile int32_t gc_cycle; |
321 | volatile int32_t nscd_certainly_running; |
322 | volatile nscd_time_t timestamp; |
323 | /* Room for extensions. */ |
324 | volatile uint32_t [4]; |
325 | |
326 | nscd_ssize_t module; |
327 | nscd_ssize_t data_size; |
328 | |
329 | nscd_ssize_t first_free; /* Offset of first free byte in data area. */ |
330 | |
331 | nscd_ssize_t nentries; |
332 | nscd_ssize_t maxnentries; |
333 | nscd_ssize_t maxnsearched; |
334 | |
335 | uint64_t poshit; |
336 | uint64_t neghit; |
337 | uint64_t posmiss; |
338 | uint64_t negmiss; |
339 | |
340 | uint64_t rdlockdelayed; |
341 | uint64_t wrlockdelayed; |
342 | |
343 | uint64_t addfailed; |
344 | |
345 | ref_t array[0]; |
346 | }; |
347 | |
348 | |
349 | /* Mapped database record. */ |
350 | struct mapped_database |
351 | { |
352 | const struct database_pers_head *head; |
353 | const char *data; |
354 | size_t mapsize; |
355 | int counter; /* > 0 indicates it is usable. */ |
356 | size_t datasize; |
357 | }; |
358 | #define NO_MAPPING ((struct mapped_database *) -1l) |
359 | |
360 | struct locked_map_ptr |
361 | { |
362 | int lock; |
363 | struct mapped_database *mapped; |
364 | }; |
365 | #define libc_locked_map_ptr(class, name) class struct locked_map_ptr name |
366 | |
367 | /* Try acquiring lock for mapptr, returns true if it succeeds, false |
368 | if not. */ |
369 | static inline bool |
370 | __nscd_acquire_maplock (volatile struct locked_map_ptr *mapptr) |
371 | { |
372 | int cnt = 0; |
373 | while (__builtin_expect (atomic_compare_and_exchange_val_acq (&mapptr->lock, |
374 | 1, 0) != 0, 0)) |
375 | { |
376 | // XXX Best number of rounds? |
377 | if (__glibc_unlikely (++cnt > 5)) |
378 | return false; |
379 | |
380 | atomic_spin_nop (); |
381 | } |
382 | |
383 | return true; |
384 | } |
385 | |
386 | |
387 | /* Open socket connection to nscd server. */ |
388 | extern int __nscd_open_socket (const char *key, size_t keylen, |
389 | request_type type, void *response, |
390 | size_t responselen) attribute_hidden; |
391 | |
392 | /* Try to get a file descriptor for the shared memory segment |
393 | containing the database. */ |
394 | extern struct mapped_database *__nscd_get_mapping (request_type type, |
395 | const char *key, |
396 | struct mapped_database **mappedp) attribute_hidden; |
397 | |
398 | /* Get reference of mapping. */ |
399 | extern struct mapped_database *__nscd_get_map_ref (request_type type, |
400 | const char *name, |
401 | volatile struct locked_map_ptr *mapptr, |
402 | int *gc_cyclep) |
403 | attribute_hidden; |
404 | |
405 | /* Unmap database. */ |
406 | extern void __nscd_unmap (struct mapped_database *mapped) |
407 | attribute_hidden; |
408 | |
409 | /* Drop reference of mapping. */ |
410 | static int |
411 | __attribute__ ((unused)) |
412 | __nscd_drop_map_ref (struct mapped_database *map, int *gc_cycle) |
413 | { |
414 | if (map != NO_MAPPING) |
415 | { |
416 | int now_cycle = map->head->gc_cycle; |
417 | if (__glibc_unlikely (now_cycle != *gc_cycle)) |
418 | { |
419 | /* We might have read inconsistent data. */ |
420 | *gc_cycle = now_cycle; |
421 | return -1; |
422 | } |
423 | |
424 | if (atomic_fetch_add_relaxed (&map->counter, -1) == 1) |
425 | __nscd_unmap (mapped: map); |
426 | } |
427 | |
428 | return 0; |
429 | } |
430 | |
431 | |
432 | /* Search the mapped database. */ |
433 | extern struct datahead *__nscd_cache_search (request_type type, |
434 | const char *key, |
435 | size_t keylen, |
436 | const struct mapped_database *mapped, |
437 | size_t datalen) |
438 | attribute_hidden; |
439 | |
440 | /* Wrappers around read, readv and write that only read/write less than LEN |
441 | bytes on error or EOF. */ |
442 | extern ssize_t __readall (int fd, void *buf, size_t len) |
443 | attribute_hidden; |
444 | extern ssize_t __readvall (int fd, const struct iovec *iov, int iovcnt) |
445 | attribute_hidden; |
446 | extern ssize_t writeall (int fd, const void *buf, size_t len) |
447 | attribute_hidden; |
448 | |
449 | /* Get netlink timestamp counter from mapped area or zero. */ |
450 | extern uint32_t __nscd_get_nl_timestamp (void) |
451 | attribute_hidden; |
452 | |
453 | #endif /* nscd.h */ |
454 | |