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