assert.h isn't used anymore, remove inclusion.
[kopensolaris-gnu/glibc.git] / nscd / grpcache.c
1 /* Cache handling for group lookup.
2    Copyright (C) 1998, 1999 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10
11    The GNU C Library 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 GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public
17    License along with the GNU C Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.  */
20
21 #include <errno.h>
22 #include <error.h>
23 #include <grp.h>
24 #include <stddef.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29
30 #include "nscd.h"
31 #include "dbg_log.h"
32
33 /* This is the standard reply in case the service is disabled.  */
34 static const gr_response_header disabled =
35 {
36   version: NSCD_VERSION,
37   found: -1,
38   gr_name_len: 0,
39   gr_passwd_len: 0,
40   gr_gid: -1,
41   gr_mem_cnt: 0,
42 };
43
44 /* This is the struct describing how to write this record.  */
45 const struct iovec grp_iov_disabled =
46 {
47   iov_base: (void *) &disabled,
48   iov_len: sizeof (disabled)
49 };
50
51
52 /* This is the standard reply in case we haven't found the dataset.  */
53 static const gr_response_header notfound =
54 {
55   version: NSCD_VERSION,
56   found: 0,
57   gr_name_len: 0,
58   gr_passwd_len: 0,
59   gr_gid: -1,
60   gr_mem_cnt: 0,
61 };
62
63 /* This is the struct describing how to write this record.  */
64 static const struct iovec iov_notfound =
65 {
66   iov_base: (void *) &notfound,
67   iov_len: sizeof (notfound)
68 };
69
70
71 struct groupdata
72 {
73   gr_response_header resp;
74   char strdata[0];
75 };
76
77
78 static void
79 cache_addgr (struct database *db, int fd, request_header *req, void *key,
80              struct group *grp)
81 {
82   ssize_t total;
83   ssize_t written;
84   time_t t = time (NULL);
85
86   if (grp == NULL)
87     {
88       /* We have no data.  This means we send the standard reply for this
89          case.  */
90       void *copy;
91
92       total = sizeof (notfound);
93
94       written = writev (fd, &iov_notfound, 1);
95
96       copy = malloc (req->key_len);
97       if (copy == NULL)
98         error (EXIT_FAILURE, errno, _("while allocating key copy"));
99       memcpy (copy, key, req->key_len);
100
101       /* Compute the timeout time.  */
102       t += db->negtimeout;
103
104       /* Now get the lock to safely insert the records.  */
105       pthread_rwlock_rdlock (&db->lock);
106
107       cache_add (req->type, copy, req->key_len, &iov_notfound,
108                  sizeof (notfound), (void *) -1, 0, t, db);
109
110       pthread_rwlock_unlock (&db->lock);
111     }
112   else
113     {
114       /* Determine the I/O structure.  */
115       struct groupdata *data;
116       size_t gr_name_len = strlen (grp->gr_name) + 1;
117       size_t gr_passwd_len = strlen (grp->gr_passwd) + 1;
118       size_t gr_mem_cnt = 0;
119       size_t *gr_mem_len;
120       size_t gr_mem_len_total = 0;
121       char *gr_name;
122       char *cp;
123       char buf[12];
124       ssize_t n;
125       size_t cnt;
126
127       /* We need this to insert the `bygid' entry.  */
128       n = snprintf (buf, sizeof (buf), "%d", grp->gr_gid) + 1;
129
130       /* Determine the length of all members.  */
131       while (grp->gr_mem[gr_mem_cnt])
132         ++gr_mem_cnt;
133       gr_mem_len = (size_t *) alloca (gr_mem_cnt * sizeof (size_t));
134       for (gr_mem_cnt = 0; grp->gr_mem[gr_mem_cnt]; ++gr_mem_cnt)
135         {
136           gr_mem_len[gr_mem_cnt] = strlen (grp->gr_mem[gr_mem_cnt]) + 1;
137           gr_mem_len_total += gr_mem_len[gr_mem_cnt];
138         }
139
140       /* We allocate all data in one memory block: the iov vector,
141          the response header and the dataset itself.  */
142       total = (sizeof (struct groupdata)
143                + gr_mem_cnt * sizeof (size_t)
144                + gr_name_len + gr_passwd_len + gr_mem_len_total);
145       data = (struct groupdata *) malloc (total + n);
146       if (data == NULL)
147         /* There is no reason to go on.  */
148         error (EXIT_FAILURE, errno, _("while allocating cache entry"));
149
150       data->resp.found = 1;
151       data->resp.gr_name_len = gr_name_len;
152       data->resp.gr_passwd_len = gr_passwd_len;
153       data->resp.gr_gid = grp->gr_gid;
154       data->resp.gr_mem_cnt = gr_mem_cnt;
155
156       cp = data->strdata;
157
158       /* This is the member string length array.  */
159       cp = mempcpy (cp, gr_mem_len, gr_mem_cnt * sizeof (size_t));
160       gr_name = cp = mempcpy (cp, grp->gr_name, gr_name_len);
161       cp = mempcpy (cp, grp->gr_passwd, gr_passwd_len);
162
163       for (cnt = 0; cnt < gr_mem_cnt; ++cnt)
164         cp = mempcpy (cp, grp->gr_mem[cnt], gr_mem_len[cnt]);
165
166       /* Finally the stringified GID value.  */
167       memcpy (cp, buf, n);
168
169       /* Write the result.  */
170       written = write (fd, &data->resp, total);
171
172       /* Compute the timeout time.  */
173       t += db->postimeout;
174
175       /* Now get the lock to safely insert the records.  */
176       pthread_rwlock_rdlock (&db->lock);
177
178       /* We have to add the value for both, byname and byuid.  */
179       cache_add (GETGRBYNAME, gr_name, gr_name_len, data,
180                  total, data, 0, t, db);
181
182       cache_add (GETGRBYGID, cp, n, data, total, data, 1, t, db);
183
184       pthread_rwlock_unlock (&db->lock);
185     }
186
187   if (written != total)
188     {
189       char buf[256];
190       dbg_log (_("short write in %s: %s"),  __FUNCTION__,
191                strerror_r (errno, buf, sizeof (buf)));
192     }
193 }
194
195
196 void
197 addgrbyname (struct database *db, int fd, request_header *req, void *key)
198 {
199   /* Search for the entry matching the key.  Please note that we don't
200      look again in the table whether the dataset is now available.  We
201      simply insert it.  It does not matter if it is in there twice.  The
202      pruning function only will look at the timestamp.  */
203   int buflen = 256;
204   char *buffer = alloca (buflen);
205   struct group resultbuf;
206   struct group *grp;
207
208   if (debug_level > 0)
209     dbg_log (_("Haven't found \"%s\" in group cache!"), key);
210
211   while (getgrnam_r (key, &resultbuf, buffer, buflen, &grp) != 0
212          && errno == ERANGE)
213     {
214       errno = 0;
215       buflen += 256;
216       buffer = alloca (buflen);
217     }
218
219   cache_addgr (db, fd, req, key, grp);
220 }
221
222
223 void
224 addgrbygid (struct database *db, int fd, request_header *req, void *key)
225 {
226   /* Search for the entry matching the key.  Please note that we don't
227      look again in the table whether the dataset is now available.  We
228      simply insert it.  It does not matter if it is in there twice.  The
229      pruning function only will look at the timestamp.  */
230   int buflen = 256;
231   char *buffer = alloca (buflen);
232   struct group resultbuf;
233   struct group *grp;
234   gid_t gid = atol (key);
235
236   if (debug_level > 0)
237     dbg_log (_("Haven't found \"%d\" in group cache!"), gid);
238
239   while (getgrgid_r (gid, &resultbuf, buffer, buflen, &grp) != 0
240          && errno == ERANGE)
241     {
242       errno = 0;
243       buflen += 256;
244       buffer = alloca (buflen);
245     }
246
247   cache_addgr (db, fd, req, key, grp);
248 }