Whamcloud - gitweb
b=15253 add failover nidlist to proc import
[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  2008 Sun Microsystems, Inc. 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;
98         struct passwd *pw;
99         struct group  *gr;
100
101         CHECK_DURATION_START;
102         pw = getpwuid((*grp)->mgd_uid);
103         CHECK_DURATION_END("getpwuid", 2);
104         if (!pw) {
105                 errlog("no such user %u\n", (*grp)->mgd_uid);
106                 (*grp)->mgd_err = errno ? errno : EIDRM;
107                 return sizeof(*param);
108         }
109         (*grp)->mgd_gid = pw->pw_gid;
110
111         maxgroups = sysconf(_SC_NGROUPS_MAX);
112         size = offsetof(struct mds_grp_downcall_data, mgd_groups[maxgroups]);
113         param = malloc(size);
114         if (param == NULL) {
115                 errlog("fail to alloc %d bytes for uid %u with %d groups\n",
116                        size, (*grp)->mgd_uid, maxgroups);
117                 return sizeof(*param);
118         }
119
120         memcpy(param, *grp, sizeof(*param));
121         param->mgd_groups[param->mgd_ngroups++] = pw->pw_gid;
122         *grp = param;
123         CHECK_DURATION_START;
124         while ((gr = getgrent())) {
125                 if (gr->gr_gid == pw->pw_gid)
126                         continue;
127                 if (!gr->gr_mem)
128                         continue;
129                 for (i = 0; gr->gr_mem[i]; i++) {
130                         if (strcmp(gr->gr_mem[i], pw->pw_name) == 0) {
131                                 param->mgd_groups[param->mgd_ngroups++] =
132                                         gr->gr_gid;
133                                 break;
134                         }
135                 }
136                 if (param->mgd_ngroups == maxgroups)
137                         break;
138         }
139         CHECK_DURATION_END("getgrent loop", 3);
140         endgrent();
141         qsort(param->mgd_groups, param->mgd_ngroups,
142               sizeof(param->mgd_groups[0]), compare_u32);
143
144         return size;
145 }
146
147 /* Note that we need to make the downcall regardless of error, so that the
148  * MDS doesn't continue to wait on the upcall. */
149 int main(int argc, char **argv)
150 {
151         int fd, rc, c, size;
152         int debug = 0, sleepy = 0, verbose = 0, print_usage = 0;
153         pid_t mypid;
154         struct mds_grp_downcall_data sparam = { MDS_GRP_DOWNCALL_MAGIC };
155         struct mds_grp_downcall_data *param = &sparam;
156         char pathname[1024], *end, *progname, *mdsname = NULL;
157
158         progname = strrchr(argv[0], '/');
159         if (progname == NULL)
160                 progname = argv[0];
161         else
162                 progname++;
163
164         if (strstr(progname, "verbose"))
165                 verbose++;
166
167         openlog(progname, LOG_PERROR, LOG_AUTHPRIV);
168
169         opterr = 0;
170         while ((c = getopt(argc, argv, "dhsv")) != -1) {
171                 switch (c) {
172                 case 'd':
173                         debug++;
174                         break;
175                 case 's':
176                         sleepy++;
177                         break;
178                 case 'v':
179                         verbose++;
180                         break;
181                 default:
182                         errlog("bad parameter '%c'\n", optopt);
183                         print_usage++;
184                 case 'h':
185                         print_usage++;
186                         break;
187                 }
188         }
189
190         /* sleep has 0 param, debug has 1 param, upcall has 2 param */
191         if (!sleepy && optind + !sleepy + !debug != argc)
192                 print_usage++;
193
194         if (print_usage) {
195                 usage(stderr, progname);
196                 return print_usage > 1 ? EINVAL : 0;
197         }
198
199         if (!sleepy) {
200                 param->mgd_uid = strtoul(argv[optind + !debug], &end, 0);
201                 if (*end) {
202                         errlog("invalid uid '%s'", argv[optind + !debug]);
203                         usage(stderr, progname);
204                         return EINVAL;
205                 }
206                 if (!debug)
207                         mdsname = argv[optind];
208         }
209
210         mypid = getpid();
211
212         if (verbose)
213                 syslog(LOG_DEBUG, "starting l_getgroups(pid %u) for uid %u\n",
214                        mypid, param->mgd_uid);
215
216         CHECK_DURATION_START;
217         size = get_groups_local(&param);
218         CHECK_DURATION_END("get_groups_local", 10);
219         if (debug) {
220                 int i;
221                 if (param->mgd_err) {
222                         if (param->mgd_err != ENXIO)
223                                 errlog("error getting uid %d groups: %s\n",
224                                        param->mgd_uid,strerror(param->mgd_err));
225                         rc = param->mgd_err;
226                 } else {
227                         printf("uid=%d gid=", param->mgd_uid);
228                         for (i = 0; i < param->mgd_ngroups; i++)
229                                 printf("%s%d", i > 0 ? "," : "",
230                                        param->mgd_groups[i]);
231                         printf("\n");
232                         rc = 0;
233                 }
234         } else if (sleepy) {
235                 rc = mlockall(MCL_CURRENT);
236                 errlog("%s all pages in RAM (pid %u): rc %d\n",
237                        rc ? "failed to lock" : "locked", mypid, rc);
238                 sleep(1000000000);
239         } else {
240                 snprintf(pathname, 1024, "/proc/fs/lustre/mds/%s/group_info",
241                          mdsname);
242                 CHECK_DURATION_START;
243                 fd = open(pathname, O_WRONLY);
244                 if (fd < 0) {
245                         errlog("can't open device %s: %s\n",
246                                pathname, strerror(errno));
247                         rc = errno;
248                 } else {
249                         rc = write(fd, param, size);
250                         if (rc > 0)
251                                 rc = 0;
252
253                         close(fd);
254                 }
255                 CHECK_DURATION_END("group_info write", 1);
256         }
257         if (verbose)
258                 syslog(LOG_DEBUG, "ending l_getgroups(pid %u) for uid %u\n",
259                        mypid, param->mgd_uid);
260
261         closelog();
262         return rc;
263 }