Whamcloud - gitweb
Changelog update
[fs/lustre-release.git] / lustre / utils / l_getgroups.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * GPL HEADER START
5  *
6  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 only,
10  * as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License version 2 for more details (a copy is included
16  * in the LICENSE file that accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License
19  * version 2 along with this program; If not, see
20  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
21  *
22  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
23  * CA 95054 USA or visit www.sun.com if you need additional information or
24  * have any questions.
25  *
26  * GPL HEADER END
27  */
28 /*
29  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
30  * Use is subject to license terms.
31  */
32 /*
33  * This file is part of Lustre, http://www.lustre.org/
34  * Lustre is a trademark of Sun Microsystems, Inc.
35  */
36
37 #include <stdlib.h>
38 #include <stdint.h>
39 #include <stdio.h>
40 #include <unistd.h>
41 #include <errno.h>
42 #include <string.h>
43 #include <fcntl.h>
44 #include <pwd.h>
45 #include <grp.h>
46 #include <stdarg.h>
47 #include <stddef.h>
48 #include <syslog.h>
49 #include <sys/mman.h>
50 #include <time.h>
51
52 #include <lustre/lustre_user.h>
53
54 #define CHECK_DURATION_START                                            \
55 do {                                                                    \
56         time_t __check_start = time(NULL)
57
58 #define CHECK_DURATION_END(str, secs)                                   \
59         if (time(NULL) > __check_start + (secs))                        \
60                 errlog("LONG OP %s: %d elapsed, %d expected\n", str,    \
61                        time(NULL) - __check_start, secs);               \
62 } while (0)
63
64 void usage(FILE *out, const char *progname)
65 {
66         fprintf(out, "\nusage: %s [-v] {-d | <mdsname>} <uid>\n"
67                      "usage: %s [-v] -s\n"
68                      "Normally invoked as an upcall from Lustre, set via:\n"
69                      "  /proc/fs/lustre/mds/{mdsname}/group_upcall\n"
70                      "\t-d: debug, print values to stdout instead of Lustre\n"
71                      "\t-s: sleep, mlock memory in core and sleep forever\n"
72                      "\t-v: verbose, log start/stop to syslog\n",
73                      progname, progname);
74 }
75
76 static int compare_u32(const void *v1, const void *v2)
77 {
78         return (*(__u32 *)v1 - *(__u32 *)v2);
79 }
80
81 static void errlog(const char *fmt, ...)
82 {
83         va_list arg, carg;
84
85         va_start(arg, fmt);
86         va_copy(carg, arg);
87         vsyslog(LOG_NOTICE, fmt, arg);
88         va_end(arg);
89
90         vfprintf(stderr, fmt, carg);
91         va_end(carg);
92 }
93
94 int get_groups_local(struct mds_grp_downcall_data **grp)
95 {
96         struct mds_grp_downcall_data *param;
97         int i, maxgroups, size, saved_errno;
98         struct passwd *pw;
99         struct group  *gr;
100
101         CHECK_DURATION_START;
102         pw = getpwuid((*grp)->mgd_uid);
103         saved_errno = errno;
104         CHECK_DURATION_END("getpwuid", 2);
105         if (!pw) {
106                 errlog("no such user %u\n", (*grp)->mgd_uid);
107                 (*grp)->mgd_err = saved_errno ? saved_errno : EIDRM;
108                 return sizeof(*param);
109         }
110         (*grp)->mgd_gid = pw->pw_gid;
111
112         maxgroups = sysconf(_SC_NGROUPS_MAX);
113         size = offsetof(struct mds_grp_downcall_data, mgd_groups[maxgroups]);
114         param = malloc(size);
115         if (param == NULL) {
116                 errlog("fail to alloc %d bytes for uid %u with %d groups\n",
117                        size, (*grp)->mgd_uid, maxgroups);
118                 return sizeof(*param);
119         }
120
121         memcpy(param, *grp, sizeof(*param));
122         param->mgd_groups[param->mgd_ngroups++] = pw->pw_gid;
123         *grp = param;
124         CHECK_DURATION_START;
125         while ((gr = getgrent())) {
126                 if (gr->gr_gid == pw->pw_gid)
127                         continue;
128                 if (!gr->gr_mem)
129                         continue;
130                 for (i = 0; gr->gr_mem[i]; i++) {
131                         if (strcmp(gr->gr_mem[i], pw->pw_name) == 0) {
132                                 param->mgd_groups[param->mgd_ngroups++] =
133                                         gr->gr_gid;
134                                 break;
135                         }
136                 }
137                 if (param->mgd_ngroups == maxgroups)
138                         break;
139         }
140         CHECK_DURATION_END("getgrent loop", 3);
141         endgrent();
142         qsort(param->mgd_groups, param->mgd_ngroups,
143               sizeof(param->mgd_groups[0]), compare_u32);
144
145         return size;
146 }
147
148 /* Note that we need to make the downcall regardless of error, so that the
149  * MDS doesn't continue to wait on the upcall. */
150 int main(int argc, char **argv)
151 {
152         int fd, rc, c, size;
153         int debug = 0, sleepy = 0, verbose = 0, print_usage = 0;
154         pid_t mypid;
155         struct mds_grp_downcall_data sparam = { MDS_GRP_DOWNCALL_MAGIC };
156         struct mds_grp_downcall_data *param = &sparam;
157         char pathname[1024], *end, *progname, *mdsname = NULL;
158
159         progname = strrchr(argv[0], '/');
160         if (progname == NULL)
161                 progname = argv[0];
162         else
163                 progname++;
164
165         if (strstr(progname, "verbose"))
166                 verbose++;
167
168         openlog(progname, LOG_PERROR, LOG_AUTHPRIV);
169
170         opterr = 0;
171         while ((c = getopt(argc, argv, "dhsv")) != -1) {
172                 switch (c) {
173                 case 'd':
174                         debug++;
175                         break;
176                 case 's':
177                         sleepy++;
178                         break;
179                 case 'v':
180                         verbose++;
181                         break;
182                 default:
183                         errlog("bad parameter '%c'\n", optopt);
184                         print_usage++;
185                 case 'h':
186                         print_usage++;
187                         break;
188                 }
189         }
190
191         /* sleep has 0 param, debug has 1 param, upcall has 2 param */
192         if (!sleepy && optind + !sleepy + !debug != argc)
193                 print_usage++;
194
195         if (print_usage) {
196                 usage(stderr, progname);
197                 return print_usage > 1 ? EINVAL : 0;
198         }
199
200         if (!sleepy) {
201                 param->mgd_uid = strtoul(argv[optind + !debug], &end, 0);
202                 if (*end) {
203                         errlog("invalid uid '%s'", argv[optind + !debug]);
204                         usage(stderr, progname);
205                         return EINVAL;
206                 }
207                 if (!debug)
208                         mdsname = argv[optind];
209         }
210
211         mypid = getpid();
212
213         if (verbose)
214                 syslog(LOG_DEBUG, "starting l_getgroups(pid %u) for uid %u\n",
215                        mypid, param->mgd_uid);
216
217         CHECK_DURATION_START;
218         size = get_groups_local(&param);
219         CHECK_DURATION_END("get_groups_local", 10);
220         if (debug) {
221                 int i;
222                 if (param->mgd_err) {
223                         if (param->mgd_err != ENXIO)
224                                 errlog("error getting uid %d groups: %s\n",
225                                        param->mgd_uid,strerror(param->mgd_err));
226                         rc = param->mgd_err;
227                 } else {
228                         printf("uid=%d gid=", param->mgd_uid);
229                         for (i = 0; i < param->mgd_ngroups; i++)
230                                 printf("%s%d", i > 0 ? "," : "",
231                                        param->mgd_groups[i]);
232                         printf("\n");
233                         rc = 0;
234                 }
235         } else if (sleepy) {
236                 rc = mlockall(MCL_CURRENT);
237                 errlog("%s all pages in RAM (pid %u): rc %d\n",
238                        rc ? "failed to lock" : "locked", mypid, rc);
239                 sleep(1000000000);
240         } else {
241                 snprintf(pathname, 1024, "/proc/fs/lustre/mds/%s/group_info",
242                          mdsname);
243                 CHECK_DURATION_START;
244                 fd = open(pathname, O_WRONLY);
245                 if (fd < 0) {
246                         errlog("can't open device %s: %s\n",
247                                pathname, strerror(errno));
248                         rc = errno;
249                 } else {
250                         rc = write(fd, param, size);
251                         if (rc > 0)
252                                 rc = 0;
253
254                         close(fd);
255                 }
256                 CHECK_DURATION_END("group_info write", 1);
257         }
258         if (verbose)
259                 syslog(LOG_DEBUG, "ending l_getgroups(pid %u) for uid %u\n",
260                        mypid, param->mgd_uid);
261
262         closelog();
263         return rc;
264 }