sys-admin 1.3.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +16 -0
- data/MANIFEST +15 -14
- data/README +54 -38
- data/examples/groups.rb +3 -3
- data/examples/users.rb +21 -3
- data/{lib/sys/unix.c → ext/admin.c} +50 -60
- data/ext/admin.h +428 -0
- data/{extconf.rb → ext/extconf.rb} +4 -8
- data/test/tc_admin.rb +1 -1
- data/test/tc_unix.rb +5 -5
- data/test/{tc_win32.rb → tc_windows.rb} +68 -4
- metadata +9 -7
- data/lib/sys/unix.h +0 -411
- data/lib/sys/win32.orig +0 -403
data/ext/admin.h
ADDED
@@ -0,0 +1,428 @@
|
|
1
|
+
#include <limits.h>
|
2
|
+
#include <stdlib.h>
|
3
|
+
#include <stdio.h>
|
4
|
+
#include <unistd.h>
|
5
|
+
#include <pwd.h>
|
6
|
+
#include <grp.h>
|
7
|
+
#include <fcntl.h>
|
8
|
+
#include <errno.h>
|
9
|
+
#include <string.h>
|
10
|
+
|
11
|
+
#define SYS_ADMIN_VERSION "1.4.0"
|
12
|
+
|
13
|
+
#ifdef HAVE_LASTLOG_H
|
14
|
+
#include <lastlog.h>
|
15
|
+
#else
|
16
|
+
#include <utmp.h>
|
17
|
+
#endif
|
18
|
+
|
19
|
+
#ifndef _POSIX_LOGIN_NAME_MAX
|
20
|
+
#define _POSIX_LOGIN_NAME_MAX 9
|
21
|
+
#endif
|
22
|
+
|
23
|
+
#ifdef _SC_GETPW_R_SIZE_MAX
|
24
|
+
#define USER_BUF_SIZE (sysconf(_SC_GETPW_R_SIZE_MAX))
|
25
|
+
#else
|
26
|
+
#define USER_BUF_SIZE 1024
|
27
|
+
#endif
|
28
|
+
|
29
|
+
#ifdef _SC_GETGR_R_SIZE_MAX
|
30
|
+
#define GROUP_BUF_SIZE (sysconf(_SC_GETGR_R_SIZE_MAX))
|
31
|
+
#else
|
32
|
+
#define GROUP_BUF_SIZE 7296
|
33
|
+
#endif
|
34
|
+
|
35
|
+
#ifndef _PATH_LASTLOG
|
36
|
+
#define _PATH_LASTLOG "/var/adm/lastlog"
|
37
|
+
#endif
|
38
|
+
|
39
|
+
/* Function prototypes */
|
40
|
+
static VALUE get_user(struct passwd* p);
|
41
|
+
static VALUE get_group(struct group* g);
|
42
|
+
int get_lastlog_info(struct passwd* p, VALUE v_value);
|
43
|
+
|
44
|
+
VALUE cUser, cGroup, cAdminError;
|
45
|
+
|
46
|
+
/*
|
47
|
+
* :no-doc:
|
48
|
+
*
|
49
|
+
* Helper function that returns a User object based on user ID.
|
50
|
+
*/
|
51
|
+
static VALUE get_user_by_num(VALUE v_uid){
|
52
|
+
VALUE v_user;
|
53
|
+
uid_t uid = NUM2INT(v_uid);
|
54
|
+
|
55
|
+
#ifdef HAVE_GETPWUID_R
|
56
|
+
char buf[USER_BUF_SIZE];
|
57
|
+
struct passwd pwd;
|
58
|
+
struct passwd* pwdbuf;
|
59
|
+
|
60
|
+
if(getpwuid_r(uid, &pwd, buf, sizeof(buf), &pwdbuf) != 0)
|
61
|
+
rb_raise(cAdminError, "%s", strerror(errno));
|
62
|
+
|
63
|
+
if(!pwdbuf)
|
64
|
+
rb_raise(cAdminError, "no user found for %i:", uid);
|
65
|
+
|
66
|
+
v_user = get_user(pwdbuf);
|
67
|
+
#elif HAVE_GETPWUID
|
68
|
+
struct passwd* pwd;
|
69
|
+
if( (pwd = getpwuid(uid)) == NULL)
|
70
|
+
rb_raise(cAdminError, "no user found for: %i", uid);
|
71
|
+
|
72
|
+
v_user = get_user(pwd);
|
73
|
+
#else
|
74
|
+
rb_raise(rb_eNotImpError, "getting user by user ID not supported");
|
75
|
+
#endif
|
76
|
+
|
77
|
+
return v_user;
|
78
|
+
}
|
79
|
+
|
80
|
+
/*
|
81
|
+
* :no-doc:
|
82
|
+
*
|
83
|
+
* Helper function that returns a User object based on name.
|
84
|
+
*/
|
85
|
+
static VALUE get_user_by_name(VALUE v_name){
|
86
|
+
VALUE v_user;
|
87
|
+
SafeStringValue(v_name);
|
88
|
+
|
89
|
+
#ifdef HAVE_GETPWNAM_R
|
90
|
+
char buf[USER_BUF_SIZE];
|
91
|
+
struct passwd pwd;
|
92
|
+
struct passwd* pwdbuf;
|
93
|
+
|
94
|
+
if(getpwnam_r(RSTRING(v_name)->ptr, &pwd, buf, sizeof(buf), &pwdbuf) != 0)
|
95
|
+
rb_raise(cAdminError, "%s", strerror(errno));
|
96
|
+
|
97
|
+
if(!pwdbuf)
|
98
|
+
rb_raise(cAdminError, "no user found for %s", StringValuePtr(v_name));
|
99
|
+
|
100
|
+
v_user = get_user(pwdbuf);
|
101
|
+
#elif HAVE_GETPWNAM
|
102
|
+
struct passwd* pwd;
|
103
|
+
if( (pwd = getpwnam(RSTRING(v_name)->ptr)) == NULL)
|
104
|
+
rb_raise(cAdminError, "no user found for %s", StringValuePtr(v_name));
|
105
|
+
|
106
|
+
v_user = get_user(pwd);
|
107
|
+
#else
|
108
|
+
rb_raise(rb_eNotImpError, "getting user by name not supported");
|
109
|
+
#endif
|
110
|
+
|
111
|
+
return v_user;
|
112
|
+
}
|
113
|
+
|
114
|
+
/*
|
115
|
+
* :no-doc:
|
116
|
+
*
|
117
|
+
* Helper function that returns a Group object based on group ID.
|
118
|
+
*/
|
119
|
+
static VALUE get_group_by_num(VALUE v_gid){
|
120
|
+
VALUE v_group;
|
121
|
+
gid_t gid = NUM2INT(v_gid);
|
122
|
+
|
123
|
+
#ifdef HAVE_GETGRGID_R
|
124
|
+
char buf[GROUP_BUF_SIZE];
|
125
|
+
struct group grp;
|
126
|
+
struct group* grpbuf;
|
127
|
+
|
128
|
+
if(getgrgid_r(gid, &grp, buf, sizeof(buf), &grpbuf) != 0)
|
129
|
+
rb_raise(cAdminError, "%s", strerror(errno));
|
130
|
+
|
131
|
+
if(!grpbuf)
|
132
|
+
rb_raise(cAdminError, "no group found for group ID: %i", gid);
|
133
|
+
|
134
|
+
v_group = get_group(grpbuf);
|
135
|
+
#elif HAVE_GETGRGID
|
136
|
+
struct group* grp;
|
137
|
+
if( (grp = getgrgid(gid)) == NULL)
|
138
|
+
rb_raise(cAdminError, "no group found for group ID: %i", gid);
|
139
|
+
|
140
|
+
v_group = get_group(grp);
|
141
|
+
#else
|
142
|
+
rb_raise(rb_eNotImpError, "getting group by group ID not supported");
|
143
|
+
#endif
|
144
|
+
|
145
|
+
return v_group;
|
146
|
+
}
|
147
|
+
|
148
|
+
/*
|
149
|
+
* :no-doc:
|
150
|
+
*
|
151
|
+
* Helper function that returns a Group object based on group name.
|
152
|
+
*/
|
153
|
+
static VALUE get_group_by_name(VALUE v_name){
|
154
|
+
VALUE v_group = Qnil;
|
155
|
+
SafeStringValue(v_name);
|
156
|
+
#ifdef HAVE_GETGRNAM_R
|
157
|
+
char buf[GROUP_BUF_SIZE];
|
158
|
+
struct group grp;
|
159
|
+
struct group* grpbuf;
|
160
|
+
|
161
|
+
if(getgrnam_r(RSTRING(v_name)->ptr, &grp, buf, sizeof(buf), &grpbuf) != 0)
|
162
|
+
rb_raise(cAdminError, "%s", strerror(errno));
|
163
|
+
|
164
|
+
if(!grpbuf)
|
165
|
+
rb_raise(cAdminError, "no group found for: %s", StringValuePtr(v_name));
|
166
|
+
|
167
|
+
v_group = get_group(grpbuf);
|
168
|
+
#elif HAVE_GETGRNAM
|
169
|
+
struct group* grp
|
170
|
+
if((grp = getgrnam(RSTRING(v_name)->ptr)) == NULL)
|
171
|
+
rb_raise(cAdminError, "no group found for: %s", StringValuePtr(v_name));
|
172
|
+
|
173
|
+
v_group = get_group(grp);
|
174
|
+
#else
|
175
|
+
rb_raise(rb_eNotImpError,"get_group is not supported on this platform");
|
176
|
+
#endif
|
177
|
+
|
178
|
+
return v_group;
|
179
|
+
}
|
180
|
+
|
181
|
+
/*
|
182
|
+
* :no-doc:
|
183
|
+
*
|
184
|
+
* Helper function that turns a struct passwd into a User object.
|
185
|
+
*/
|
186
|
+
static VALUE get_user(struct passwd* pwd){
|
187
|
+
VALUE v_user = rb_funcall(cUser, rb_intern("new"), 0, 0);
|
188
|
+
|
189
|
+
rb_iv_set(v_user, "@name", rb_str_new2(pwd->pw_name));
|
190
|
+
rb_iv_set(v_user, "@uid", INT2FIX(pwd->pw_uid));
|
191
|
+
rb_iv_set(v_user, "@gid", INT2FIX(pwd->pw_gid));
|
192
|
+
rb_iv_set(v_user, "@dir", rb_str_new2(pwd->pw_dir));
|
193
|
+
rb_iv_set(v_user, "@shell", rb_str_new2(pwd->pw_shell));
|
194
|
+
|
195
|
+
#ifdef HAVE_ST_PW_PASSWD
|
196
|
+
rb_iv_set(v_user, "@passwd", rb_str_new2(pwd->pw_passwd));
|
197
|
+
#endif
|
198
|
+
|
199
|
+
/* TODO: Fix this, or just set it to nil */
|
200
|
+
#ifdef HAVE_ST_PW_AGE
|
201
|
+
rb_iv_set(v_user, "@age", INT2FIX(pwd->pw_age));
|
202
|
+
#endif
|
203
|
+
|
204
|
+
#ifdef HAVE_ST_PW_COMMENT
|
205
|
+
rb_iv_set(v_user, "@comment", rb_str_new2(pwd->pw_comment));
|
206
|
+
#endif
|
207
|
+
|
208
|
+
#ifdef HAVE_ST_PW_GECOS
|
209
|
+
rb_iv_set(v_user, "@gecos", rb_str_new2(pwd->pw_gecos));
|
210
|
+
#endif
|
211
|
+
|
212
|
+
#ifdef HAVE_ST_PW_QUOTA
|
213
|
+
rb_iv_set(v_user, "@quota", INT2FIX(pwd->pw_quota));
|
214
|
+
#endif
|
215
|
+
|
216
|
+
#ifdef HAVE_ST_PW_CLASS
|
217
|
+
rb_iv_set(v_user, "@class", rb_str_new2(pwd->pw_class));
|
218
|
+
#endif
|
219
|
+
|
220
|
+
#ifdef HAVE_ST_PW_EXPIRE
|
221
|
+
rb_iv_set(v_user, "@expire", rb_time_new(pwd->pw_expire, 0));
|
222
|
+
#endif
|
223
|
+
|
224
|
+
#ifdef HAVE_ST_PW_CHANGE
|
225
|
+
rb_iv_set(v_user, "@change", rb_time_new(pwd->pw_change, 0));
|
226
|
+
#endif
|
227
|
+
|
228
|
+
/* Get the lastlog info for the given user */
|
229
|
+
get_lastlog_info(pwd, v_user);
|
230
|
+
|
231
|
+
return v_user;
|
232
|
+
}
|
233
|
+
|
234
|
+
/*
|
235
|
+
* :no-doc:
|
236
|
+
*
|
237
|
+
* Helper function that turns a User object into a struct passwd.
|
238
|
+
*/
|
239
|
+
void get_user_from_value(VALUE v_user, struct passwd* pwd){
|
240
|
+
|
241
|
+
VALUE v_name = rb_iv_get(v_user, "@name");
|
242
|
+
VALUE v_uid = rb_iv_get(v_user, "@uid");
|
243
|
+
VALUE v_gid = rb_iv_get(v_user, "@gid");
|
244
|
+
VALUE v_dir = rb_iv_get(v_user, "@dir");
|
245
|
+
VALUE v_shell = rb_iv_get(v_user, "@shell");
|
246
|
+
|
247
|
+
if(NIL_P(v_name))
|
248
|
+
rb_raise(cAdminError, "user name cannot be nil");
|
249
|
+
|
250
|
+
if(!NIL_P(v_uid))
|
251
|
+
pwd->pw_uid = NUM2INT(v_uid);
|
252
|
+
|
253
|
+
if(!NIL_P(v_gid))
|
254
|
+
pwd->pw_gid = NUM2INT(v_gid);
|
255
|
+
|
256
|
+
if(!NIL_P(v_dir)){
|
257
|
+
SafeStringValue(v_dir);
|
258
|
+
pwd->pw_dir = StringValuePtr(v_dir);
|
259
|
+
}
|
260
|
+
|
261
|
+
if(!NIL_P(v_shell)){
|
262
|
+
SafeStringValue(v_shell);
|
263
|
+
pwd->pw_shell = StringValuePtr(v_shell);
|
264
|
+
}
|
265
|
+
|
266
|
+
#ifdef HAVE_ST_PW_PASSWD
|
267
|
+
VALUE v_passwd = rb_iv_get(v_user, "@passwd");
|
268
|
+
if(!NIL_P(v_passwd)){
|
269
|
+
SafeStringValue(v_passwd);
|
270
|
+
pwd->pw_passwd = StringValuePtr(v_passwd);
|
271
|
+
}
|
272
|
+
#endif
|
273
|
+
|
274
|
+
/* TODO: Fix this or just set it to nil */
|
275
|
+
#ifdef HAVE_ST_PW_AGE
|
276
|
+
VALUE v_age = rb_iv_get(v_user, "@age");
|
277
|
+
if(!NIL_P(v_age))
|
278
|
+
pwd->pw_age = (char*)NUM2INT(v_age);
|
279
|
+
#endif
|
280
|
+
|
281
|
+
#ifdef HAVE_ST_PW_COMMENT
|
282
|
+
VALUE v_comment = rb_iv_get(v_user, "@comment");
|
283
|
+
if(!NIL_P(v_comment)){
|
284
|
+
SafeStringValue(v_comment);
|
285
|
+
pwd->pw_comment = StringValuePtr(v_comment);
|
286
|
+
}
|
287
|
+
#endif
|
288
|
+
|
289
|
+
#ifdef HAVE_ST_PW_GECOS
|
290
|
+
VALUE v_gecos = rb_iv_get(v_user, "@gecos");
|
291
|
+
if(!NIL_P(v_gecos)){
|
292
|
+
SafeStringValue(v_gecos);
|
293
|
+
pwd->pw_gecos = StringValuePtr(v_gecos);
|
294
|
+
}
|
295
|
+
#endif
|
296
|
+
|
297
|
+
#ifdef HAVE_ST_PW_QUOTA
|
298
|
+
VALUE v_quota = rb_iv_get(v_user, "@quota");
|
299
|
+
if(!NIL_P(v_quota))
|
300
|
+
pwd->pw_quota = NUM2INT(v_quota);
|
301
|
+
#endif
|
302
|
+
|
303
|
+
#ifdef HAVE_ST_PW_CLASS
|
304
|
+
VALUE v_class = rb_iv_get(v_user, "@class");
|
305
|
+
if(!NIL_P(v_class)){
|
306
|
+
SafeStringValue(v_class);
|
307
|
+
pwd->pw_class = StringValuePtr(v_class);
|
308
|
+
}
|
309
|
+
#endif
|
310
|
+
|
311
|
+
#ifdef HAVE_ST_PW_EXPIRE
|
312
|
+
VALUE v_expire = rb_iv_get(v_user, "@expire");
|
313
|
+
v_expire = rb_funcall(v_expire, rb_intern("to_i"), 0, 0);
|
314
|
+
if(!NIL_P(v_expire))
|
315
|
+
pwd->pw_expire = NUM2ULONG(v_expire);
|
316
|
+
#endif
|
317
|
+
|
318
|
+
#ifdef HAVE_ST_PW_CHANGE
|
319
|
+
VALUE v_change = rb_iv_get(v_user, "@change");
|
320
|
+
v_change = rb_funcall(v_change, rb_intern("to_i"), 0, 0);
|
321
|
+
if(!NIL_P(v_change))
|
322
|
+
pwd->pw_change = NUM2ULONG(v_change);
|
323
|
+
#endif
|
324
|
+
|
325
|
+
}
|
326
|
+
|
327
|
+
/*
|
328
|
+
* :no-doc:
|
329
|
+
*
|
330
|
+
* Helper function that turns a struct grp into a Group object.
|
331
|
+
*/
|
332
|
+
static VALUE get_group(struct group* g){
|
333
|
+
VALUE v_group = rb_funcall(cGroup,rb_intern("new"),0,0);
|
334
|
+
VALUE v_array = rb_ary_new();
|
335
|
+
|
336
|
+
/* Return the members as an Array of Strings */
|
337
|
+
while(*g->gr_mem){
|
338
|
+
rb_ary_push(v_array, rb_str_new2(*g->gr_mem));
|
339
|
+
g->gr_mem++;
|
340
|
+
}
|
341
|
+
|
342
|
+
rb_iv_set(v_group, "@name", rb_str_new2(g->gr_name));
|
343
|
+
rb_iv_set(v_group, "@gid", INT2FIX(g->gr_gid));
|
344
|
+
rb_iv_set(v_group, "@members", v_array);
|
345
|
+
#ifdef HAVE_ST_GR_PASSWD
|
346
|
+
rb_iv_set(v_group, "@passwd", rb_str_new2(g->gr_passwd));
|
347
|
+
#endif
|
348
|
+
|
349
|
+
return v_group;
|
350
|
+
}
|
351
|
+
|
352
|
+
/*
|
353
|
+
* :no-doc:
|
354
|
+
*
|
355
|
+
* Helper function that turns a Group object into a struct group.
|
356
|
+
*/
|
357
|
+
void get_group_from_value(VALUE v_group, struct group* grp){
|
358
|
+
char** members = malloc(sizeof(char*));
|
359
|
+
VALUE v_name = rb_iv_get(v_group, "@name");
|
360
|
+
VALUE v_gid = rb_iv_get(v_group, "@gid");
|
361
|
+
VALUE v_mem = rb_iv_get(v_group, "@members");
|
362
|
+
VALUE v_passwd = rb_iv_get(v_group, "@passwd");
|
363
|
+
int i = 0;
|
364
|
+
|
365
|
+
if(NIL_P(v_name))
|
366
|
+
rb_raise(cAdminError, "group name must be set");
|
367
|
+
|
368
|
+
SafeStringValue(v_name);
|
369
|
+
grp->gr_name = StringValuePtr(v_name);
|
370
|
+
|
371
|
+
if(!NIL_P(v_gid))
|
372
|
+
grp->gr_gid = NUM2INT(v_gid);
|
373
|
+
|
374
|
+
if(!NIL_P(v_mem)){
|
375
|
+
VALUE v_value;
|
376
|
+
while((v_value = rb_ary_shift(v_mem)) != Qnil){
|
377
|
+
members[i] = StringValuePtr(v_value);
|
378
|
+
i++;
|
379
|
+
}
|
380
|
+
members[i] = '\0';
|
381
|
+
grp->gr_mem = members;
|
382
|
+
}
|
383
|
+
|
384
|
+
#ifdef HAVE_ST_GR_PASSWD
|
385
|
+
if(!NIL_P(v_passwd)){
|
386
|
+
SafeStringValue(v_passwd);
|
387
|
+
grp->gr_passwd = StringValuePtr(v_passwd);
|
388
|
+
}
|
389
|
+
#endif
|
390
|
+
|
391
|
+
free(members);
|
392
|
+
}
|
393
|
+
|
394
|
+
/*
|
395
|
+
* :no-doc:
|
396
|
+
*
|
397
|
+
* Helper function that gets lastlog information for the User object.
|
398
|
+
*/
|
399
|
+
int get_lastlog_info(struct passwd* pwd, VALUE v_user){
|
400
|
+
int fd;
|
401
|
+
ssize_t bytes_read;
|
402
|
+
struct lastlog log;
|
403
|
+
int ll_size = sizeof(struct lastlog);
|
404
|
+
|
405
|
+
/* The lastlog information is not necessarily readable by all users, so
|
406
|
+
* ignore open() errors if they occur.
|
407
|
+
*/
|
408
|
+
if((fd = open(_PATH_LASTLOG, O_RDONLY)) == -1)
|
409
|
+
return -1;
|
410
|
+
|
411
|
+
if((bytes_read = pread(fd, &log, ll_size, pwd->pw_uid * ll_size)) < 0){
|
412
|
+
close(fd);
|
413
|
+
rb_raise(cAdminError, "%s", strerror(errno));
|
414
|
+
}
|
415
|
+
|
416
|
+
close(fd);
|
417
|
+
|
418
|
+
if(bytes_read > 0){
|
419
|
+
#ifdef HAVE_ST_LL_TIME
|
420
|
+
if(log.ll_time != 0)
|
421
|
+
rb_iv_set(v_user, "@login_time", rb_time_new(log.ll_time, 0));
|
422
|
+
#endif
|
423
|
+
rb_iv_set(v_user, "@login_device", rb_str_new2(log.ll_line));
|
424
|
+
rb_iv_set(v_user, "@login_host", rb_str_new2(log.ll_host));
|
425
|
+
}
|
426
|
+
|
427
|
+
return 0;
|
428
|
+
}
|
@@ -1,13 +1,12 @@
|
|
1
1
|
require "mkmf"
|
2
|
-
require "ftools"
|
3
2
|
|
4
|
-
if
|
3
|
+
if RUBY_PLATFORM.match('mswin')
|
5
4
|
STDERR.puts "Use the install.rb file on Win32 systems."
|
6
5
|
STDERR.puts "Exiting. The sys-admin package was NOT installed."
|
7
6
|
exit
|
8
7
|
else
|
9
|
-
|
10
|
-
|
8
|
+
dir_config('admin')
|
9
|
+
|
11
10
|
have_func("getlogin_r")
|
12
11
|
have_func("getlogin")
|
13
12
|
have_func("getenv")
|
@@ -51,12 +50,9 @@ else
|
|
51
50
|
end
|
52
51
|
|
53
52
|
$CFLAGS += " -D_POSIX_PTHREAD_SEMANTICS"
|
54
|
-
if
|
53
|
+
if RUBY_PLATFORM.match("linux")
|
55
54
|
$CFLAGS += " -D_GNU_SOURCE -D_REENTRANT"
|
56
55
|
end
|
57
|
-
|
58
|
-
File.copy("lib/sys/unix.c", "admin.c")
|
59
|
-
File.copy("lib/sys/unix.h", "admin.h")
|
60
56
|
end
|
61
57
|
|
62
58
|
create_makefile("sys/admin")
|