(doc): It's "Name Service" not "Name Switch".
[kopensolaris-gnu/glibc.git] / nscd / pwdcache.c
1 /* Cache handling for passwd lookup.
2    Copyright (C) 1998 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 <pwd.h>
24 #include <stddef.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <time.h>
29 #include <unistd.h>
30
31 #include "nscd.h"
32 #include "dbg_log.h"
33
34 /* This is the standard reply in case the service is disabled.  */
35 static const pw_response_header disabled =
36 {
37   version: NSCD_VERSION,
38   found: -1,
39   pw_name_len: 0,
40   pw_passwd_len: 0,
41   pw_uid: -1,
42   pw_gid: -1,
43   pw_gecos_len: 0,
44   pw_dir_len: 0,
45   pw_shell_len: 0
46 };
47
48 /* This is the struct describing how to write this record.  */
49 const struct iovec pwd_iov_disabled =
50 {
51   iov_base: (void *) &disabled,
52   iov_len: sizeof (disabled)
53 };
54
55
56 /* This is the standard reply in case we haven't found the dataset.  */
57 static const pw_response_header notfound =
58 {
59   version: NSCD_VERSION,
60   found: 0,
61   pw_name_len: 0,
62   pw_passwd_len: 0,
63   pw_uid: -1,
64   pw_gid: -1,
65   pw_gecos_len: 0,
66   pw_dir_len: 0,
67   pw_shell_len: 0
68 };
69
70 /* This is the struct describing how to write this record.  */
71 static const struct iovec iov_notfound =
72 {
73   iov_base: (void *) &notfound,
74   iov_len: sizeof (notfound)
75 };
76
77
78 struct passwddata
79 {
80   pw_response_header resp;
81   char strdata[0];
82 };
83
84
85 static void
86 cache_addpw (struct database *db, int fd, request_header *req, void *key,
87              struct passwd *pwd)
88 {
89   ssize_t total;
90   ssize_t written;
91   time_t t = time (NULL);
92
93   if (pwd == NULL)
94     {
95       /* We have no data.  This means we send the standard reply for this
96          case.  */
97       void *copy;
98
99       total = sizeof (notfound);
100
101       written = writev (fd, &iov_notfound, 1);
102
103       copy = malloc (req->key_len);
104       if (copy == NULL)
105         error (EXIT_FAILURE, errno, _("while allocating key copy"));
106       memcpy (copy, key, req->key_len);
107
108       /* Compute the timeout time.  */
109       t += db->negtimeout;
110
111       /* Now get the lock to safely insert the records.  */
112       pthread_rwlock_rdlock (&db->lock);
113
114       cache_add (req->type, copy, req->key_len, &iov_notfound,
115                  sizeof (notfound), (void *) -1, 0, t, db);
116
117       pthread_rwlock_unlock (&db->lock);
118     }
119   else
120     {
121       /* Determine the I/O structure.  */
122       struct passwddata *data;
123       size_t pw_name_len = strlen (pwd->pw_name) + 1;
124       size_t pw_passwd_len = strlen (pwd->pw_passwd) + 1;
125       size_t pw_gecos_len = strlen (pwd->pw_gecos) + 1;
126       size_t pw_dir_len = strlen (pwd->pw_dir) + 1;
127       size_t pw_shell_len = strlen (pwd->pw_shell) + 1;
128       char *cp;
129       char buf[12];
130       ssize_t n;
131
132       /* We need this to insert the `byuid' entry.  */
133       n = snprintf (buf, sizeof (buf), "%d", pwd->pw_uid) + 1;
134
135       /* We allocate all data in one memory block: the iov vector,
136          the response header and the dataset itself.  */
137       total = (sizeof (struct passwddata) + pw_name_len + pw_passwd_len
138                + pw_gecos_len + pw_dir_len + pw_shell_len);
139       data = (struct passwddata *) malloc (total + n);
140       if (data == NULL)
141         /* There is no reason to go on.  */
142         error (EXIT_FAILURE, errno, _("while allocating cache entry"));
143
144       data->resp.found = 1;
145       data->resp.pw_name_len = pw_name_len;
146       data->resp.pw_passwd_len = pw_passwd_len;
147       data->resp.pw_uid = pwd->pw_uid;
148       data->resp.pw_gid = pwd->pw_gid;
149       data->resp.pw_gecos_len = pw_gecos_len;
150       data->resp.pw_dir_len = pw_dir_len;
151       data->resp.pw_shell_len = pw_shell_len;
152
153       cp = data->strdata;
154
155       /* Copy the strings over into the buffer.  */
156       cp = mempcpy (cp, pwd->pw_name, pw_name_len);
157       cp = mempcpy (cp, pwd->pw_passwd, pw_passwd_len);
158       cp = mempcpy (cp, pwd->pw_gecos, pw_gecos_len);
159       cp = mempcpy (cp, pwd->pw_dir, pw_dir_len);
160       cp = mempcpy (cp, pwd->pw_shell, pw_shell_len);
161
162       /* Finally the stringified UID value.  */
163       memcpy (cp, buf, n);
164
165       /* We write the dataset before inserting it to the database
166          since while inserting this thread might block and so would
167          unnecessarily let the receiver wait.  */
168       written = write (fd, &data->resp, total);
169
170       /* Compute the timeout time.  */
171       t += db->postimeout;
172
173       /* Now get the lock to safely insert the records.  */
174       pthread_rwlock_rdlock (&db->lock);
175
176       /* We have to add the value for both, byname and byuid.  */
177       cache_add (GETPWBYNAME, data->strdata, pw_name_len, data,
178                  total, data, 0, t, db);
179
180       cache_add (GETPWBYUID, cp, n, data, total, data, 1, t, db);
181
182       pthread_rwlock_unlock (&db->lock);
183     }
184
185   if (written != total)
186     {
187       char buf[256];
188       dbg_log (_("short write in %s: %s"),  __FUNCTION__,
189                strerror_r (errno, buf, sizeof (buf)));
190     }
191 }
192
193
194 void
195 addpwbyname (struct database *db, int fd, request_header *req, void *key)
196 {
197   /* Search for the entry matching the key.  Please note that we don't
198      look again in the table whether the dataset is now available.  We
199      simply insert it.  It does not matter if it is in there twice.  The
200      pruning function only will look at the timestamp.  */
201   int buflen = 256;
202   char *buffer = alloca (buflen);
203   struct passwd resultbuf;
204   struct passwd *pwd;
205
206   if (debug_level > 0)
207     dbg_log (_("Haven't found \"%s\" in password cache!"), key);
208
209   while (getpwnam_r (key, &resultbuf, buffer, buflen, &pwd) != 0
210          && errno == ERANGE)
211     {
212       errno = 0;
213       buflen += 256;
214       buffer = alloca (buflen);
215     }
216
217   cache_addpw (db, fd, req, key, pwd);
218 }
219
220
221 void
222 addpwbyuid (struct database *db, int fd, request_header *req, void *key)
223 {
224   /* Search for the entry matching the key.  Please note that we don't
225      look again in the table whether the dataset is now available.  We
226      simply insert it.  It does not matter if it is in there twice.  The
227      pruning function only will look at the timestamp.  */
228   int buflen = 256;
229   char *buffer = alloca (buflen);
230   struct passwd resultbuf;
231   struct passwd *pwd;
232   uid_t uid = atol (key);
233
234   if (debug_level > 0)
235     dbg_log (_("Haven't found \"%d\" in password cache!"), uid);
236
237   while (getpwuid_r (uid, &resultbuf, buffer, buflen, &pwd) != 0
238          && errno == ERANGE)
239     {
240       errno = 0;
241       buflen += 256;
242       buffer = alloca (buflen);
243     }
244
245   cache_addpw (db, fd, req, key, pwd);
246 }