etcutils 1.0.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.
- checksums.yaml +15 -0
- data/.gitignore +22 -0
- data/.travis.yml +19 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +20 -0
- data/LICENSE +20 -0
- data/README.md +335 -0
- data/Rakefile +16 -0
- data/etcutils.gemspec +27 -0
- data/ext/etcutils/etcutils.c +1398 -0
- data/ext/etcutils/etcutils.h +189 -0
- data/ext/etcutils/extconf.rb +51 -0
- data/ext/etcutils/group.c +192 -0
- data/ext/etcutils/passwd.c +297 -0
- data/lib/etcutils.rb +4 -0
- data/lib/etcutils/version.rb +3 -0
- data/tests/README +141 -0
- data/tests/etcutils_test_helper.rb +8 -0
- data/tests/root/etc_utils.rb +5 -0
- data/tests/root/gshadow_tests.rb +153 -0
- data/tests/root/locking.rb +23 -0
- data/tests/root/shadow_tests.rb +161 -0
- data/tests/test_etc_utils.rb +107 -0
- data/tests/test_eu_locking.rb +15 -0
- data/tests/test_eu_next_uid_next_gid.rb +32 -0
- data/tests/test_eu_sgetpwent.rb +91 -0
- data/tests/test_group_class.rb +128 -0
- data/tests/test_passwd_class.rb +125 -0
- data/tests/user/etc_utils.rb +6 -0
- data/tests/user/locking.rb +7 -0
- metadata +119 -0
data/etcutils.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require 'etcutils/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "etcutils"
|
7
|
+
spec.version = EtcUtils::VERSION
|
8
|
+
spec.platform = Gem::Platform::RUBY
|
9
|
+
spec.authors = ["David Campbell"]
|
10
|
+
spec.email = "david@mrcampbell.org"
|
11
|
+
spec.description = "Ruby Extension that allows for reads and writes to the /etc user db."
|
12
|
+
spec.summary = %q{This gem is specific to *nix environments, allowing for reads and writes to passwd,shadow,group, and gshadow /etc files. It is inspired by Std-lib Etc and the Gem libshadow-ruby however modified to use classes rather than structs to allow for future growth. There are probably still bugs, so use at your own risk.}
|
13
|
+
spec.homepage = "https://github.com/dacamp/etcutils"
|
14
|
+
spec.licenses = ["MIT"]
|
15
|
+
|
16
|
+
spec.extensions = ['ext/etcutils/extconf.rb']
|
17
|
+
spec.files = `git ls-files`.split($/)
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = [ 'lib', 'ext' ]
|
20
|
+
|
21
|
+
|
22
|
+
spec.has_rdoc = false
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler"
|
25
|
+
spec.add_development_dependency "rake"
|
26
|
+
spec.add_development_dependency "rake-compiler"
|
27
|
+
end
|
@@ -0,0 +1,1398 @@
|
|
1
|
+
/********************************************************************
|
2
|
+
|
3
|
+
Ruby C Extension for read write access to the Linux user database
|
4
|
+
|
5
|
+
Copyright (C) 2013 David Campbell
|
6
|
+
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
a copy of this software and associated documentation files (the
|
9
|
+
"Software"), to deal in the Software without restriction, including
|
10
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
the following conditions:
|
14
|
+
|
15
|
+
The above copyright notice and this permission notice shall be
|
16
|
+
included in all copies or substantial portions of the Software.
|
17
|
+
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
22
|
+
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
23
|
+
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
24
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
25
|
+
SOFTWARE.
|
26
|
+
|
27
|
+
********************************************************************/
|
28
|
+
#include <unistd.h>
|
29
|
+
#include <time.h>
|
30
|
+
#include "etcutils.h"
|
31
|
+
|
32
|
+
VALUE mEtcUtils;
|
33
|
+
ID id_name, id_passwd, id_uid, id_gid;
|
34
|
+
static VALUE uid_global;
|
35
|
+
static VALUE gid_global;
|
36
|
+
static VALUE assigned_uids;
|
37
|
+
static VALUE assigned_gids;
|
38
|
+
|
39
|
+
/* Start of helper functions */
|
40
|
+
VALUE next_uid(int argc, VALUE *argv, VALUE self)
|
41
|
+
{
|
42
|
+
VALUE i;
|
43
|
+
uid_t req;
|
44
|
+
|
45
|
+
rb_scan_args(argc, argv, "01", &i);
|
46
|
+
if (NIL_P(i))
|
47
|
+
i = uid_global;
|
48
|
+
|
49
|
+
i = rb_Integer(i);
|
50
|
+
req = NUM2UINT(i);
|
51
|
+
|
52
|
+
if ( req > ((unsigned int)65533) )
|
53
|
+
rb_raise(rb_eArgError, "UID must be between 0 and 65533");
|
54
|
+
|
55
|
+
while ( getpwuid(req) || rb_ary_includes(assigned_uids, UINT2NUM(req)) ) req++;
|
56
|
+
if (!argc)
|
57
|
+
rb_ary_push(assigned_uids, UINT2NUM(req));
|
58
|
+
else
|
59
|
+
uid_global = UINT2NUM(req);
|
60
|
+
|
61
|
+
return UINT2NUM(req);
|
62
|
+
}
|
63
|
+
|
64
|
+
VALUE next_gid(int argc, VALUE *argv, VALUE self)
|
65
|
+
{
|
66
|
+
VALUE i;
|
67
|
+
gid_t req;
|
68
|
+
|
69
|
+
rb_scan_args(argc, argv, "01", &i);
|
70
|
+
if (NIL_P(i))
|
71
|
+
i = gid_global;
|
72
|
+
|
73
|
+
i = rb_Integer(i);
|
74
|
+
req = NUM2UINT(i);
|
75
|
+
|
76
|
+
if ( req > ((unsigned int)65533) )
|
77
|
+
rb_raise(rb_eArgError, "GID must be between 0 and 65533");
|
78
|
+
|
79
|
+
while ( getgrgid(req) || rb_ary_includes(assigned_gids, UINT2NUM(req)) ) req++;
|
80
|
+
if (!argc)
|
81
|
+
rb_ary_push(assigned_gids, UINT2NUM(req));
|
82
|
+
else
|
83
|
+
gid_global = UINT2NUM(req);
|
84
|
+
|
85
|
+
return UINT2NUM(req);
|
86
|
+
}
|
87
|
+
|
88
|
+
VALUE iv_get_time(VALUE self, const char *name)
|
89
|
+
{
|
90
|
+
VALUE e;
|
91
|
+
time_t t;
|
92
|
+
e = rb_iv_get(self, name);
|
93
|
+
|
94
|
+
if (NIL_P(e) || NUM2INT(e) < 0)
|
95
|
+
return Qnil;
|
96
|
+
|
97
|
+
t = NUM2INT(e) * 86400;
|
98
|
+
return rb_time_new(t, 0);
|
99
|
+
}
|
100
|
+
|
101
|
+
VALUE iv_set_time(VALUE self, VALUE v, const char *name)
|
102
|
+
{
|
103
|
+
struct timeval t;
|
104
|
+
long int d;
|
105
|
+
|
106
|
+
RTIME_VAL(t) = rb_time_timeval(v);
|
107
|
+
d = ((long)t.tv_sec / 86400);
|
108
|
+
|
109
|
+
if (FIXNUM_P(v) && d == 0 && t.tv_sec == NUM2INT(v))
|
110
|
+
d = NUM2INT(v);
|
111
|
+
else if (d < 1)
|
112
|
+
d = -1;
|
113
|
+
|
114
|
+
return rb_iv_set(self, name, INT2NUM(d));
|
115
|
+
}
|
116
|
+
|
117
|
+
VALUE rb_current_time()
|
118
|
+
{
|
119
|
+
time_t s;
|
120
|
+
s = time(NULL);
|
121
|
+
return rb_time_new(s, ((time_t)0));
|
122
|
+
}
|
123
|
+
|
124
|
+
void
|
125
|
+
eu_errno(VALUE str)
|
126
|
+
{
|
127
|
+
/*
|
128
|
+
SafeStringValue(str);
|
129
|
+
if ( (errno) && ( !(errno == ENOTTY) || !(errno == ENOENT) ) )
|
130
|
+
rb_sys_fail( StringValuePtr(str) );
|
131
|
+
Errno::ENOTTY: Inappropriate ioctl for device
|
132
|
+
https://bugs.ruby-lang.org/issues/6127
|
133
|
+
ioctl range error in 1.9.3
|
134
|
+
Fixed in 1.9.3-p194 (REVISION r37138)
|
135
|
+
Ubuntu System Ruby (via APT) - 1.9.3-p0 (REVISION 33570)
|
136
|
+
errno = 0;
|
137
|
+
*/
|
138
|
+
}
|
139
|
+
|
140
|
+
void ensure_eu_type(VALUE self, VALUE klass)
|
141
|
+
{
|
142
|
+
if (!rb_obj_is_kind_of(self, klass))
|
143
|
+
rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)",
|
144
|
+
rb_obj_classname(self), rb_class2name(klass));
|
145
|
+
}
|
146
|
+
|
147
|
+
void ensure_file(VALUE io)
|
148
|
+
{
|
149
|
+
rb_io_check_initialized(RFILE(io)->fptr);
|
150
|
+
}
|
151
|
+
|
152
|
+
void ensure_writes(VALUE io, int t)
|
153
|
+
{
|
154
|
+
ensure_file(io);
|
155
|
+
if (!( ((RFILE(io)->fptr)->mode) & t ))
|
156
|
+
rb_raise(rb_eIOError, "not opened for writing");
|
157
|
+
}
|
158
|
+
|
159
|
+
|
160
|
+
|
161
|
+
/* Validate (s)group members/admins
|
162
|
+
void confirm_members(char ** mem)
|
163
|
+
{
|
164
|
+
char *name;
|
165
|
+
|
166
|
+
while(name = *mem++)
|
167
|
+
if (!getpwnam(name))
|
168
|
+
rb_raise(rb_eArgError,
|
169
|
+
"%s was not found in '%s'", name, PASSWD);
|
170
|
+
}
|
171
|
+
*/
|
172
|
+
|
173
|
+
void free_char_members(char ** mem, int c)
|
174
|
+
{
|
175
|
+
if (NULL != mem) {
|
176
|
+
int i;
|
177
|
+
for (i=0; i<c+1 ; i++) free(mem[i]);
|
178
|
+
free(mem);
|
179
|
+
}
|
180
|
+
}
|
181
|
+
|
182
|
+
char** setup_char_members(VALUE ary)
|
183
|
+
{
|
184
|
+
char ** mem;
|
185
|
+
VALUE tmp,last;
|
186
|
+
long i,off;
|
187
|
+
Check_Type(ary,T_ARRAY);
|
188
|
+
|
189
|
+
mem = malloc((RARRAY_LEN(ary) + 1)*sizeof(char**));
|
190
|
+
if (mem == NULL)
|
191
|
+
rb_memerror();
|
192
|
+
|
193
|
+
rb_ary_sort_bang(ary);
|
194
|
+
last = rb_str_new2("");
|
195
|
+
off = 0;
|
196
|
+
|
197
|
+
for (i = 0; i < RARRAY_LEN(ary); i++) {
|
198
|
+
tmp = rb_obj_as_string(RARRAY_PTR(ary)[i]);
|
199
|
+
if ( (rb_str_cmp(tmp, last)) ) {
|
200
|
+
StringValueCStr(tmp);
|
201
|
+
mem[i-off] = malloc((RSTRING_LEN(tmp))*sizeof(char*));;
|
202
|
+
|
203
|
+
if (mem[i-off] == NULL) rb_memerror();
|
204
|
+
strcpy(mem[i-off], RSTRING_PTR(tmp));
|
205
|
+
} else
|
206
|
+
off++;
|
207
|
+
|
208
|
+
last = tmp;
|
209
|
+
}
|
210
|
+
mem[i-off] = NULL;
|
211
|
+
|
212
|
+
return mem;
|
213
|
+
}
|
214
|
+
|
215
|
+
VALUE setup_safe_str(const char *str)
|
216
|
+
{
|
217
|
+
return rb_tainted_str_new2(str); // this already handles characters >= 0
|
218
|
+
}
|
219
|
+
|
220
|
+
VALUE setup_safe_array(char **arr)
|
221
|
+
{
|
222
|
+
VALUE mem = rb_ary_new();
|
223
|
+
|
224
|
+
while (*arr)
|
225
|
+
rb_ary_push(mem, setup_safe_str(*arr++));
|
226
|
+
return mem;
|
227
|
+
}
|
228
|
+
/* End of helper functions */
|
229
|
+
|
230
|
+
/* set/end syscalls */
|
231
|
+
VALUE eu_setpwent(VALUE self)
|
232
|
+
{
|
233
|
+
#ifdef HAVE_SETPWENT
|
234
|
+
setpwent();
|
235
|
+
#endif
|
236
|
+
return Qnil;
|
237
|
+
}
|
238
|
+
|
239
|
+
VALUE eu_endpwent(VALUE self)
|
240
|
+
{
|
241
|
+
#ifdef HAVE_ENDPWENT
|
242
|
+
endpwent();
|
243
|
+
#endif
|
244
|
+
return Qnil;
|
245
|
+
}
|
246
|
+
|
247
|
+
VALUE eu_setspent(VALUE self)
|
248
|
+
{
|
249
|
+
#ifdef HAVE_SETSPENT
|
250
|
+
setspent();
|
251
|
+
#endif
|
252
|
+
return Qnil;
|
253
|
+
}
|
254
|
+
|
255
|
+
VALUE eu_endspent(VALUE self)
|
256
|
+
{
|
257
|
+
#ifdef HAVE_ENDSPENT
|
258
|
+
endspent();
|
259
|
+
#endif
|
260
|
+
return Qnil;
|
261
|
+
}
|
262
|
+
|
263
|
+
VALUE eu_setsgent(VALUE self)
|
264
|
+
{
|
265
|
+
#ifdef HAVE_SETSGENT
|
266
|
+
setsgent();
|
267
|
+
#endif
|
268
|
+
return Qnil;
|
269
|
+
}
|
270
|
+
|
271
|
+
VALUE eu_endsgent(VALUE self)
|
272
|
+
{
|
273
|
+
#ifdef HAVE_ENDSGENT
|
274
|
+
endsgent();
|
275
|
+
#endif
|
276
|
+
return Qnil;
|
277
|
+
}
|
278
|
+
|
279
|
+
VALUE eu_setgrent(VALUE self)
|
280
|
+
{
|
281
|
+
#ifdef HAVE_SETGRENT
|
282
|
+
setgrent();
|
283
|
+
#endif
|
284
|
+
return Qnil;
|
285
|
+
}
|
286
|
+
|
287
|
+
VALUE eu_endgrent(VALUE self)
|
288
|
+
{
|
289
|
+
#ifdef HAVE_ENDGRENT
|
290
|
+
endgrent();
|
291
|
+
#endif
|
292
|
+
return Qnil;
|
293
|
+
}
|
294
|
+
|
295
|
+
/* INPUT Examples:
|
296
|
+
* - CURRENT USERS
|
297
|
+
* - bin:x:2:2:bin:/bin:/bin/bash
|
298
|
+
* - bin:x:::bin:/bin:/bin/bash
|
299
|
+
* - bin:x:::bin:/bin:/bin/sh
|
300
|
+
* - bin:x:::bin:/bin:/bin/sh
|
301
|
+
* - bin:x:::Bin User:/bin:/bin/sh
|
302
|
+
*
|
303
|
+
* CURRENT USER
|
304
|
+
* iff *one* of uid/gid is empty
|
305
|
+
* - if VAL is NOT equal to VAL in PASSWD
|
306
|
+
* - if VAL is available
|
307
|
+
* - Populate VAL
|
308
|
+
* - else raise error
|
309
|
+
* - else Populate VAL
|
310
|
+
* if PASSWORD, UID, GID, GECOS, HOMEDIR, SHELL are empty
|
311
|
+
* - populate VALUE from PASSWD
|
312
|
+
*/
|
313
|
+
VALUE eu_parsecurrent(VALUE str, VALUE ary)
|
314
|
+
{
|
315
|
+
struct passwd *pwd;
|
316
|
+
pwd = getpwnam( StringValuePtr(str) );
|
317
|
+
|
318
|
+
// Password
|
319
|
+
str = rb_ary_entry(ary,1);
|
320
|
+
if ( ! rb_eql( setup_safe_str(pwd->pw_passwd), str) )
|
321
|
+
pwd->pw_passwd = StringValuePtr(str);
|
322
|
+
|
323
|
+
// UID/GID
|
324
|
+
if ( ! RSTRING_BLANK_P( (str = rb_ary_entry(ary,2)) ) ) {
|
325
|
+
str = rb_Integer( str );
|
326
|
+
if ( ! rb_eql( INT2FIX(pwd->pw_uid), str ) )
|
327
|
+
pwd->pw_uid = NUM2UIDT(str);
|
328
|
+
}
|
329
|
+
|
330
|
+
if ( ! RSTRING_BLANK_P( (str = rb_ary_entry(ary,3)) ) ) {
|
331
|
+
str = rb_Integer( str );
|
332
|
+
if ( getgrgid(NUM2GIDT(str)) )
|
333
|
+
pwd->pw_gid = NUM2GIDT(str);
|
334
|
+
}
|
335
|
+
|
336
|
+
// GECOS
|
337
|
+
str = rb_ary_entry(ary,4);
|
338
|
+
if ( ! rb_eql( setup_safe_str(pwd->pw_gecos), str) )
|
339
|
+
pwd->pw_gecos = StringValuePtr(str);
|
340
|
+
|
341
|
+
// Directory
|
342
|
+
str = rb_ary_entry(ary,5);
|
343
|
+
if ( ! rb_eql( setup_safe_str(pwd->pw_dir), str) )
|
344
|
+
pwd->pw_dir = StringValuePtr(str);
|
345
|
+
|
346
|
+
// Shell
|
347
|
+
str = rb_ary_entry(ary,6);
|
348
|
+
if ( ! rb_eql( setup_safe_str(pwd->pw_shell), str) ) {
|
349
|
+
SafeStringValue(str);
|
350
|
+
pwd->pw_shell = StringValuePtr(str);
|
351
|
+
}
|
352
|
+
|
353
|
+
return setup_passwd(pwd);
|
354
|
+
}
|
355
|
+
|
356
|
+
/* INPUT Examples:
|
357
|
+
* - NEW USERS
|
358
|
+
* - newuser:x:1000:1000:New User:/home/newuser:/bin/bash
|
359
|
+
* - newuser:x:::New User:/home/newuser:/bin/bash
|
360
|
+
* - newuser:x:::New User::/bin/bash
|
361
|
+
*
|
362
|
+
*/
|
363
|
+
VALUE eu_parsenew(VALUE self, VALUE ary)
|
364
|
+
{
|
365
|
+
VALUE uid, gid, tmp, nam;
|
366
|
+
struct passwd *pwd;
|
367
|
+
struct group *grp;
|
368
|
+
int i = 0;
|
369
|
+
|
370
|
+
pwd = malloc(sizeof *pwd);
|
371
|
+
|
372
|
+
nam = rb_ary_entry(ary,i++);
|
373
|
+
pwd->pw_name = StringValuePtr(nam);
|
374
|
+
|
375
|
+
/* Setup password field
|
376
|
+
* if PASSWORD is empty
|
377
|
+
* - if SHADOW
|
378
|
+
* - PASSWORD equals 'x'
|
379
|
+
* - else
|
380
|
+
* - PASSWORD equals '*'
|
381
|
+
*/
|
382
|
+
tmp = rb_ary_entry(ary,i++);
|
383
|
+
if (RSTRING_BLANK_P(tmp))
|
384
|
+
tmp = PW_DEFAULT_PASS;
|
385
|
+
|
386
|
+
pwd->pw_passwd = StringValuePtr(tmp);
|
387
|
+
|
388
|
+
/* Setup UID field */
|
389
|
+
uid = rb_ary_entry(ary,i++);
|
390
|
+
gid = rb_ary_entry(ary,i++);
|
391
|
+
|
392
|
+
/* if UID Test availability */
|
393
|
+
if (! RSTRING_BLANK_P(uid))
|
394
|
+
next_uid(1, &uid, self);
|
395
|
+
|
396
|
+
uid = next_uid(0, 0, self);
|
397
|
+
|
398
|
+
/* if GID empty
|
399
|
+
* - if USERNAME found in /etc/group
|
400
|
+
* - GID equals struct group->gid
|
401
|
+
* - else next_gid
|
402
|
+
* else if GID < 1000
|
403
|
+
* - assign GID
|
404
|
+
* - else
|
405
|
+
* - next_gid
|
406
|
+
*/
|
407
|
+
if (RSTRING_BLANK_P(gid))
|
408
|
+
if ( (grp = getgrnam( StringValuePtr(nam) )) ) // Found a group with the same name
|
409
|
+
gid = GIDT2NUM(grp->gr_gid);
|
410
|
+
else {
|
411
|
+
next_gid(1, &uid, self);
|
412
|
+
gid = next_gid(0, 0, self);
|
413
|
+
}
|
414
|
+
else {
|
415
|
+
tmp = rb_Integer(gid);
|
416
|
+
if ( (NUM2UINT(tmp) != 0) && ( NUM2UINT(tmp) < ((unsigned int)1000)) )
|
417
|
+
gid = tmp;
|
418
|
+
else {
|
419
|
+
next_gid(1, &tmp, self);
|
420
|
+
gid = next_gid(0, 0, self);
|
421
|
+
}
|
422
|
+
}
|
423
|
+
|
424
|
+
pwd->pw_uid = NUM2UIDT(uid);
|
425
|
+
pwd->pw_gid = NUM2GIDT(gid);
|
426
|
+
|
427
|
+
/* _launchservicesd:*:239:239::0:0:_launchservicesd:/var/empty:/usr/bin/false */
|
428
|
+
/* daemon:x:1:1:daemon:/usr/sbin:/bin/sh */
|
429
|
+
#ifdef HAVE_ST_PW_CLASS
|
430
|
+
if ( RSTRING_BLANK_P(tmp = rb_ary_entry(ary, i)) )
|
431
|
+
tmp = setup_safe_str("");
|
432
|
+
pwd->pw_class = StringValuePtr( tmp );
|
433
|
+
|
434
|
+
i++;
|
435
|
+
#endif
|
436
|
+
|
437
|
+
#ifdef HAVE_ST_PW_CHANGE
|
438
|
+
if ( RSTRING_BLANK_P(tmp = rb_ary_entry(ary,i)) )
|
439
|
+
tmp = setup_safe_str("0");
|
440
|
+
|
441
|
+
pwd->pw_change = (time_t)NUM2UIDT((VALUE)rb_Integer( tmp ));
|
442
|
+
i++;
|
443
|
+
#endif
|
444
|
+
|
445
|
+
|
446
|
+
#ifdef HAVE_ST_PW_EXPIRE
|
447
|
+
if ( RSTRING_BLANK_P(tmp = rb_ary_entry(ary,i)) )
|
448
|
+
tmp = setup_safe_str("0");
|
449
|
+
|
450
|
+
pwd->pw_expire = (time_t)NUM2UIDT((VALUE)rb_Integer( tmp ));
|
451
|
+
i++;
|
452
|
+
#endif
|
453
|
+
|
454
|
+
/* if GECOS, HOMEDIR, SHELL is empty
|
455
|
+
* - GECOS defaults to USERNAME
|
456
|
+
* - Assign default VALUE (Need to set config VALUES)
|
457
|
+
*/
|
458
|
+
if ( RSTRING_BLANK_P(tmp = rb_ary_entry(ary,i)) )
|
459
|
+
tmp = nam;
|
460
|
+
pwd->pw_gecos = StringValuePtr( tmp );
|
461
|
+
i++;
|
462
|
+
|
463
|
+
if ( RSTRING_BLANK_P(tmp = rb_ary_entry(ary,i)) )
|
464
|
+
tmp = rb_str_plus(setup_safe_str("/home/"), nam);
|
465
|
+
pwd->pw_dir = StringValuePtr( tmp );
|
466
|
+
i++;
|
467
|
+
|
468
|
+
|
469
|
+
/* This might be a null, indicating that the system default
|
470
|
+
* should be used.
|
471
|
+
*/
|
472
|
+
if (RSTRING_BLANK_P(tmp = rb_ary_entry(ary,i)) )
|
473
|
+
tmp = setup_safe_str(DEFAULT_SHELL);
|
474
|
+
pwd->pw_shell = StringValuePtr( tmp );
|
475
|
+
|
476
|
+
tmp = setup_passwd(pwd);
|
477
|
+
|
478
|
+
if (pwd)
|
479
|
+
free(pwd);
|
480
|
+
return tmp;
|
481
|
+
}
|
482
|
+
/* End of set/end syscalls */
|
483
|
+
|
484
|
+
/* INPUT Examples:
|
485
|
+
* - CURRENT USERS
|
486
|
+
* - bin:x:2:2:bin:/bin:/bin/bash
|
487
|
+
* - bin:x:::bin:/bin:/bin/bash
|
488
|
+
* - bin:x:::bin:/bin:/bin/sh
|
489
|
+
* - bin:x:::bin:/bin:/bin/sh
|
490
|
+
* - bin:x:::Bin User:/bin:/bin/sh
|
491
|
+
*
|
492
|
+
* - NEW USERS
|
493
|
+
* - newuser:x:1000:1000:New User:/home/newuser:/bin/bash
|
494
|
+
* - newuser:x:::New User:/home/newuser:/bin/bash
|
495
|
+
* - newuser:x:::New User::/bin/bash
|
496
|
+
*
|
497
|
+
* UNIVERSAL BEHAVIOR
|
498
|
+
* if USERNAME empty
|
499
|
+
* - raise error
|
500
|
+
* CURRENT USER
|
501
|
+
* iff one of uid/gid is empty
|
502
|
+
* - if VAL is NOT equal to VAL in PASSWD
|
503
|
+
* - if VAL is available
|
504
|
+
* - Populate VAL
|
505
|
+
* - else raise error
|
506
|
+
* - else Populate VAL
|
507
|
+
* if PASSWORD, UID, GID, GECOS, HOMEDIR, SHELL are empty
|
508
|
+
* - populate VALUE from PASSWD
|
509
|
+
* NEW USER
|
510
|
+
* if UID/GID are empty
|
511
|
+
* - next_uid/next_gid
|
512
|
+
* else
|
513
|
+
* - Test VALUE availability
|
514
|
+
* then
|
515
|
+
* - Populate UID/GID
|
516
|
+
* if GECOS, HOMEDIR, SHELL is empty
|
517
|
+
* - GECOS defaults to USERNAME
|
518
|
+
* - Assign default VALUE (Need to set config VALUES)
|
519
|
+
* if PASSWORD is empty
|
520
|
+
* - raise Error
|
521
|
+
*
|
522
|
+
*/
|
523
|
+
VALUE eu_sgetpwent(VALUE self, VALUE str)
|
524
|
+
{
|
525
|
+
VALUE ary;
|
526
|
+
|
527
|
+
eu_setpwent(self);
|
528
|
+
eu_setgrent(self);
|
529
|
+
|
530
|
+
ary = rb_str_split(str, ":");
|
531
|
+
str = rb_ary_entry(ary,0);
|
532
|
+
|
533
|
+
if (RSTRING_BLANK_P(str))
|
534
|
+
rb_raise(rb_eArgError,"User name must be present.");
|
535
|
+
|
536
|
+
if (getpwnam( StringValuePtr(str) ))
|
537
|
+
return eu_parsecurrent(str, ary);
|
538
|
+
else
|
539
|
+
return eu_parsenew(self, ary);
|
540
|
+
}
|
541
|
+
|
542
|
+
VALUE eu_sgetspent(VALUE self, VALUE nam)
|
543
|
+
{
|
544
|
+
#ifdef SHADOW
|
545
|
+
struct spwd *shadow;
|
546
|
+
|
547
|
+
SafeStringValue(nam);
|
548
|
+
if ( !(shadow = sgetspent(StringValuePtr(nam))) )
|
549
|
+
rb_raise(rb_eArgError,
|
550
|
+
"can't parse %s into EtcUtils::Shadow", StringValuePtr(nam));
|
551
|
+
|
552
|
+
return setup_shadow(shadow);
|
553
|
+
#else
|
554
|
+
return Qnil;
|
555
|
+
#endif
|
556
|
+
}
|
557
|
+
|
558
|
+
// name:passwd:gid:members
|
559
|
+
static VALUE eu_grp_cur(VALUE str, VALUE ary)
|
560
|
+
{
|
561
|
+
struct group *grp;
|
562
|
+
grp = getgrnam( StringValuePtr(str) );
|
563
|
+
|
564
|
+
// Password
|
565
|
+
str = rb_ary_entry(ary,1);
|
566
|
+
if ( ! rb_eql( setup_safe_str(grp->gr_passwd), str) )
|
567
|
+
grp->gr_passwd = StringValuePtr(str);
|
568
|
+
|
569
|
+
// GID
|
570
|
+
if ( ! RSTRING_BLANK_P( (str = rb_ary_entry(ary,2)) ) ) {
|
571
|
+
str = rb_Integer( str );
|
572
|
+
if ( !getgrgid(NUM2GIDT(str)) )
|
573
|
+
grp->gr_gid = NUM2GIDT(str);
|
574
|
+
}
|
575
|
+
|
576
|
+
// Group Members
|
577
|
+
if ( RSTRING_BLANK_P( (str = rb_ary_entry(ary,3)) ))
|
578
|
+
str = rb_str_new2("");
|
579
|
+
|
580
|
+
ary = rb_str_split(str,",");
|
581
|
+
if ( ! rb_eql( setup_safe_array(grp->gr_mem), ary) )
|
582
|
+
grp->gr_mem = setup_char_members( ary );
|
583
|
+
|
584
|
+
return setup_group(grp);
|
585
|
+
}
|
586
|
+
|
587
|
+
static VALUE eu_grp_new(VALUE self, VALUE ary)
|
588
|
+
{
|
589
|
+
VALUE gid, tmp, nam;
|
590
|
+
struct passwd *pwd;
|
591
|
+
struct group *grp;
|
592
|
+
|
593
|
+
grp = malloc(sizeof *grp);
|
594
|
+
|
595
|
+
nam = rb_ary_entry(ary,0);
|
596
|
+
grp->gr_name = StringValuePtr(nam);
|
597
|
+
|
598
|
+
/* Setup password field
|
599
|
+
* if PASSWORD is empty
|
600
|
+
* - if SHADOW
|
601
|
+
* - PASSWORD equals 'x'
|
602
|
+
* - else
|
603
|
+
* - PASSWORD equals '*'
|
604
|
+
*/
|
605
|
+
tmp = rb_ary_entry(ary,1);
|
606
|
+
if (RSTRING_BLANK_P(tmp))
|
607
|
+
tmp = PW_DEFAULT_PASS;
|
608
|
+
|
609
|
+
grp->gr_passwd = StringValuePtr(tmp);
|
610
|
+
|
611
|
+
/* Setup GID field */
|
612
|
+
gid = rb_ary_entry(ary,2);
|
613
|
+
|
614
|
+
/* if GID empty
|
615
|
+
* - if USERNAME found in /etc/group
|
616
|
+
* - GID equals struct group->gid
|
617
|
+
* - else next_gid
|
618
|
+
* else
|
619
|
+
* - if UID value (as GID) found in /etc/group
|
620
|
+
* - next_gid
|
621
|
+
* - else
|
622
|
+
* - Test availability
|
623
|
+
*/
|
624
|
+
if (RSTRING_BLANK_P(gid))
|
625
|
+
if ( (pwd = getpwnam( StringValuePtr(nam) )) ) // Found a group with the same name
|
626
|
+
gid = GIDT2NUM(pwd->pw_gid);
|
627
|
+
else
|
628
|
+
gid = next_gid(0, 0, self);
|
629
|
+
else {
|
630
|
+
if ( getgrgid( (gid_t) gid ) )
|
631
|
+
next_gid(1, &gid, self);
|
632
|
+
else if ( (tmp = rb_Integer( gid )) )
|
633
|
+
next_gid(1, &tmp, self);
|
634
|
+
|
635
|
+
gid = next_gid(0, 0, self);
|
636
|
+
}
|
637
|
+
|
638
|
+
grp->gr_gid = NUM2GIDT(gid);
|
639
|
+
|
640
|
+
if (RSTRING_BLANK_P(tmp = rb_ary_entry(ary,3)))
|
641
|
+
tmp = rb_str_new2("");
|
642
|
+
grp->gr_mem = setup_char_members( rb_str_split(tmp,",") );
|
643
|
+
|
644
|
+
nam = setup_group(grp);
|
645
|
+
free_char_members(grp->gr_mem, (int)RARRAY_LEN(rb_str_split(tmp,",")));
|
646
|
+
|
647
|
+
if (grp)
|
648
|
+
free(grp);
|
649
|
+
return nam;
|
650
|
+
}
|
651
|
+
|
652
|
+
// name:passwd:gid:members
|
653
|
+
VALUE eu_sgetgrent(VALUE self, VALUE str)
|
654
|
+
{
|
655
|
+
VALUE ary;
|
656
|
+
|
657
|
+
eu_setpwent(self);
|
658
|
+
eu_setgrent(self);
|
659
|
+
|
660
|
+
ary = rb_str_split(str, ":");
|
661
|
+
str = rb_ary_entry(ary,0);
|
662
|
+
|
663
|
+
if (RSTRING_BLANK_P(str))
|
664
|
+
rb_raise(rb_eArgError,"Group name must be present.");
|
665
|
+
|
666
|
+
if (getgrnam( StringValuePtr(str) ))
|
667
|
+
return eu_grp_cur(str, ary);
|
668
|
+
else
|
669
|
+
return eu_grp_new(self, ary);
|
670
|
+
}
|
671
|
+
|
672
|
+
VALUE eu_sgetsgent(VALUE self, VALUE nam)
|
673
|
+
{
|
674
|
+
#ifdef GSHADOW
|
675
|
+
struct sgrp *gshadow;
|
676
|
+
|
677
|
+
SafeStringValue(nam);
|
678
|
+
if ( !(gshadow = sgetsgent(StringValuePtr(nam))) )
|
679
|
+
rb_raise(rb_eArgError,
|
680
|
+
"can't parse %s into EtcUtils::GShadow", StringValuePtr(nam));
|
681
|
+
|
682
|
+
return setup_gshadow(gshadow);
|
683
|
+
#else
|
684
|
+
return Qnil;
|
685
|
+
#endif
|
686
|
+
}
|
687
|
+
|
688
|
+
|
689
|
+
/* fget* functions are not available on OSx/BSD based OSes */
|
690
|
+
#ifdef HAVE_FGETGRENT
|
691
|
+
static VALUE
|
692
|
+
eu_fgetgrent(VALUE self, VALUE io)
|
693
|
+
{
|
694
|
+
struct group *grp;
|
695
|
+
|
696
|
+
ensure_file(io);
|
697
|
+
if ( (grp = fgetgrent(RFILE_FPTR(io))) == NULL )
|
698
|
+
return Qnil;
|
699
|
+
|
700
|
+
return setup_group(grp);
|
701
|
+
}
|
702
|
+
#endif
|
703
|
+
|
704
|
+
#ifdef HAVE_FGETPWENT
|
705
|
+
static VALUE
|
706
|
+
eu_fgetpwent(VALUE self, VALUE io)
|
707
|
+
{
|
708
|
+
struct passwd *pwd;
|
709
|
+
|
710
|
+
ensure_file(io);
|
711
|
+
if ( (pwd = fgetpwent(RFILE_FPTR(io))) == NULL )
|
712
|
+
return Qnil;
|
713
|
+
|
714
|
+
return setup_passwd(pwd);
|
715
|
+
}
|
716
|
+
#endif
|
717
|
+
|
718
|
+
#ifdef HAVE_FGETSPENT
|
719
|
+
static VALUE
|
720
|
+
eu_fgetspent(VALUE self, VALUE io)
|
721
|
+
{
|
722
|
+
struct spwd *spwd;
|
723
|
+
|
724
|
+
ensure_file(io);
|
725
|
+
if ( (spwd = fgetspent(RFILE_FPTR(io))) == NULL )
|
726
|
+
return Qnil;
|
727
|
+
|
728
|
+
return setup_shadow(spwd);
|
729
|
+
}
|
730
|
+
#endif
|
731
|
+
|
732
|
+
#ifdef HAVE_FGETSGENT
|
733
|
+
static VALUE
|
734
|
+
eu_fgetsgent(VALUE self, VALUE io)
|
735
|
+
{
|
736
|
+
struct sgrp *sgroup;
|
737
|
+
|
738
|
+
ensure_file(io);
|
739
|
+
if ( (sgroup = fgetsgent(RFILE_FPTR(io))) == NULL )
|
740
|
+
return Qnil;
|
741
|
+
|
742
|
+
return setup_gshadow(sgroup);
|
743
|
+
}
|
744
|
+
#endif
|
745
|
+
|
746
|
+
VALUE eu_getpwd(VALUE self, VALUE v)
|
747
|
+
{
|
748
|
+
struct passwd *strt;
|
749
|
+
eu_setpwent(self);
|
750
|
+
|
751
|
+
if ( FIXNUM_P(v) )
|
752
|
+
strt = getpwuid(NUM2UIDT(v));
|
753
|
+
else {
|
754
|
+
SafeStringValue(v);
|
755
|
+
strt = getpwnam(StringValuePtr(v));
|
756
|
+
}
|
757
|
+
|
758
|
+
if (!strt)
|
759
|
+
return Qnil;
|
760
|
+
|
761
|
+
return setup_passwd(strt);
|
762
|
+
}
|
763
|
+
|
764
|
+
VALUE eu_getspwd(VALUE self, VALUE v)
|
765
|
+
{
|
766
|
+
#ifdef SHADOW
|
767
|
+
struct spwd *strt;
|
768
|
+
eu_setspent(self);
|
769
|
+
|
770
|
+
if ( FIXNUM_P(v) ) {
|
771
|
+
struct passwd *s;
|
772
|
+
if ( (s = getpwuid(NUM2UIDT(v))) )
|
773
|
+
v = rb_str_new2(s->pw_name);
|
774
|
+
else
|
775
|
+
return Qnil;
|
776
|
+
}
|
777
|
+
|
778
|
+
SafeStringValue(v);
|
779
|
+
strt = getspnam(StringValuePtr(v));
|
780
|
+
|
781
|
+
if (!strt)
|
782
|
+
return Qnil;
|
783
|
+
|
784
|
+
return setup_shadow(strt);
|
785
|
+
#else
|
786
|
+
return Qnil;
|
787
|
+
#endif
|
788
|
+
}
|
789
|
+
|
790
|
+
VALUE eu_getsgrp(VALUE self, VALUE v)
|
791
|
+
{
|
792
|
+
#ifdef GSHADOW
|
793
|
+
struct sgrp *strt;
|
794
|
+
eu_setsgent(self);
|
795
|
+
|
796
|
+
if ( FIXNUM_P(v) ) {
|
797
|
+
struct group *s;
|
798
|
+
if ( (s = getgrgid(NUM2UIDT(v))) )
|
799
|
+
v = setup_safe_str(s->gr_name);
|
800
|
+
}
|
801
|
+
|
802
|
+
SafeStringValue(v);
|
803
|
+
strt = getsgnam(StringValuePtr(v));
|
804
|
+
|
805
|
+
if (!strt)
|
806
|
+
return Qnil;
|
807
|
+
|
808
|
+
return setup_gshadow(strt);
|
809
|
+
#else
|
810
|
+
return Qnil;
|
811
|
+
#endif
|
812
|
+
}
|
813
|
+
|
814
|
+
VALUE eu_getgrp(VALUE self, VALUE v)
|
815
|
+
{
|
816
|
+
struct group *strt;
|
817
|
+
eu_setgrent(self);
|
818
|
+
|
819
|
+
if (FIXNUM_P(v))
|
820
|
+
strt = getgrgid(NUM2UIDT(v));
|
821
|
+
else {
|
822
|
+
SafeStringValue(v);
|
823
|
+
strt = getgrnam(StringValuePtr(v));
|
824
|
+
}
|
825
|
+
|
826
|
+
if (!strt)
|
827
|
+
return Qnil;
|
828
|
+
return setup_group(strt);
|
829
|
+
}
|
830
|
+
|
831
|
+
static VALUE
|
832
|
+
eu_putpwent(VALUE mod, VALUE entry, VALUE io)
|
833
|
+
{
|
834
|
+
return user_putpwent(entry,io);
|
835
|
+
}
|
836
|
+
|
837
|
+
#ifdef SHADOW
|
838
|
+
static VALUE
|
839
|
+
eu_putspent(VALUE mod, VALUE entry, VALUE io)
|
840
|
+
{
|
841
|
+
return user_putspent(entry,io);
|
842
|
+
}
|
843
|
+
#endif
|
844
|
+
|
845
|
+
static VALUE
|
846
|
+
eu_putgrent(VALUE mod, VALUE entry, VALUE io)
|
847
|
+
{
|
848
|
+
return group_putgrent(entry,io);
|
849
|
+
}
|
850
|
+
|
851
|
+
#ifdef GSHADOW
|
852
|
+
static VALUE
|
853
|
+
eu_putsgent(VALUE mod, VALUE entry, VALUE io)
|
854
|
+
{
|
855
|
+
return group_putsgent(entry,io);
|
856
|
+
}
|
857
|
+
#endif
|
858
|
+
|
859
|
+
#ifdef HAVE_LCKPWDF
|
860
|
+
static VALUE
|
861
|
+
eu_locked_p(VALUE self)
|
862
|
+
{
|
863
|
+
int i;
|
864
|
+
errno = 0;
|
865
|
+
i = lckpwdf();
|
866
|
+
if (errno)
|
867
|
+
rb_raise(rb_eSystemCallError, "Error locking passwd files: %s", strerror(errno));
|
868
|
+
|
869
|
+
if (i)
|
870
|
+
return Qtrue;
|
871
|
+
else if (!ulckpwdf())
|
872
|
+
return Qfalse;
|
873
|
+
else
|
874
|
+
rb_raise(rb_eIOError,"Unable to determine the locked state of password files");
|
875
|
+
}
|
876
|
+
#endif
|
877
|
+
|
878
|
+
#ifdef HAVE_LCKPWDF
|
879
|
+
static VALUE
|
880
|
+
eu_lckpwdf(VALUE self)
|
881
|
+
{
|
882
|
+
VALUE r;
|
883
|
+
if ( !(r = eu_locked_p(self)) ) {
|
884
|
+
if ( !(lckpwdf()) )
|
885
|
+
r = Qtrue;
|
886
|
+
}
|
887
|
+
return r;
|
888
|
+
}
|
889
|
+
#endif
|
890
|
+
|
891
|
+
#ifdef HAVE_ULCKPWDF
|
892
|
+
static VALUE
|
893
|
+
eu_ulckpwdf(VALUE self)
|
894
|
+
{
|
895
|
+
VALUE r;
|
896
|
+
if ( (r = eu_locked_p(self)) )
|
897
|
+
if ( !(ulckpwdf()) )
|
898
|
+
r = Qtrue;
|
899
|
+
return r;
|
900
|
+
}
|
901
|
+
|
902
|
+
static int in_lock = 0;
|
903
|
+
|
904
|
+
static VALUE
|
905
|
+
lock_ensure(void)
|
906
|
+
{
|
907
|
+
ulckpwdf();
|
908
|
+
in_lock = (int)Qfalse;
|
909
|
+
return Qnil;
|
910
|
+
}
|
911
|
+
|
912
|
+
static VALUE
|
913
|
+
eu_lock(VALUE self)
|
914
|
+
{
|
915
|
+
if (eu_lckpwdf(self)) {
|
916
|
+
if (rb_block_given_p()) {
|
917
|
+
if (in_lock)
|
918
|
+
rb_raise(rb_eRuntimeError, "parallel lock iteration");
|
919
|
+
rb_ensure(rb_yield, Qnil, lock_ensure, 0);
|
920
|
+
return Qnil;
|
921
|
+
}
|
922
|
+
return Qtrue;
|
923
|
+
} else
|
924
|
+
rb_raise(rb_eIOError, "unable to create file lock");
|
925
|
+
}
|
926
|
+
|
927
|
+
static VALUE
|
928
|
+
eu_unlock(VALUE self)
|
929
|
+
{
|
930
|
+
return eu_ulckpwdf(self);
|
931
|
+
}
|
932
|
+
#endif
|
933
|
+
|
934
|
+
#ifdef SHADOW
|
935
|
+
static int spwd_block = 0;
|
936
|
+
|
937
|
+
static VALUE shadow_iterate(void)
|
938
|
+
{
|
939
|
+
struct spwd *shadow;
|
940
|
+
|
941
|
+
setspent();
|
942
|
+
while ( (shadow = getspent()) )
|
943
|
+
rb_yield(setup_shadow(shadow));
|
944
|
+
|
945
|
+
return Qnil;
|
946
|
+
}
|
947
|
+
|
948
|
+
static VALUE shadow_ensure(void)
|
949
|
+
{
|
950
|
+
endspent();
|
951
|
+
spwd_block = (int)Qfalse;
|
952
|
+
return Qnil;
|
953
|
+
}
|
954
|
+
|
955
|
+
static void each_shadow(void)
|
956
|
+
{
|
957
|
+
if (spwd_block)
|
958
|
+
rb_raise(rb_eRuntimeError, "parallel shadow iteration");
|
959
|
+
spwd_block = (int)Qtrue;
|
960
|
+
rb_ensure(shadow_iterate, 0, shadow_ensure, 0);
|
961
|
+
}
|
962
|
+
|
963
|
+
VALUE eu_getspent(VALUE self)
|
964
|
+
{
|
965
|
+
struct spwd *shadow;
|
966
|
+
|
967
|
+
if (rb_block_given_p())
|
968
|
+
each_shadow();
|
969
|
+
else if ( (shadow = getspent()) )
|
970
|
+
return setup_shadow(shadow);
|
971
|
+
return Qnil;
|
972
|
+
}
|
973
|
+
#endif
|
974
|
+
|
975
|
+
#ifdef PASSWD
|
976
|
+
static int pwd_block = 0;
|
977
|
+
|
978
|
+
static VALUE pwd_iterate(void)
|
979
|
+
{
|
980
|
+
struct passwd *pwd;
|
981
|
+
|
982
|
+
setpwent();
|
983
|
+
while ( (pwd = getpwent()) )
|
984
|
+
rb_yield(setup_passwd(pwd));
|
985
|
+
return Qnil;
|
986
|
+
}
|
987
|
+
|
988
|
+
static VALUE pwd_ensure(void)
|
989
|
+
{
|
990
|
+
endpwent();
|
991
|
+
pwd_block = (int)Qfalse;
|
992
|
+
return Qnil;
|
993
|
+
}
|
994
|
+
|
995
|
+
static void each_passwd(void)
|
996
|
+
{
|
997
|
+
if (pwd_block)
|
998
|
+
rb_raise(rb_eRuntimeError, "parallel passwd iteration");
|
999
|
+
pwd_block = (int)Qtrue;
|
1000
|
+
rb_ensure(pwd_iterate, 0, pwd_ensure, 0);
|
1001
|
+
}
|
1002
|
+
|
1003
|
+
VALUE eu_getpwent(VALUE self)
|
1004
|
+
{
|
1005
|
+
struct passwd *pwd;
|
1006
|
+
|
1007
|
+
if (rb_block_given_p())
|
1008
|
+
each_passwd();
|
1009
|
+
else if ( (pwd = getpwent()) )
|
1010
|
+
return setup_passwd(pwd);
|
1011
|
+
return Qnil;
|
1012
|
+
}
|
1013
|
+
#endif
|
1014
|
+
|
1015
|
+
#ifdef GROUP
|
1016
|
+
static int grp_block = 0;
|
1017
|
+
|
1018
|
+
static VALUE grp_iterate(void)
|
1019
|
+
{
|
1020
|
+
struct group *grp;
|
1021
|
+
|
1022
|
+
setgrent();
|
1023
|
+
while ( (grp = getgrent()) ) {
|
1024
|
+
rb_yield(setup_group(grp));
|
1025
|
+
}
|
1026
|
+
return Qnil;
|
1027
|
+
}
|
1028
|
+
|
1029
|
+
static VALUE grp_ensure(void)
|
1030
|
+
{
|
1031
|
+
endgrent();
|
1032
|
+
grp_block = (int)Qfalse;
|
1033
|
+
return Qnil;
|
1034
|
+
}
|
1035
|
+
|
1036
|
+
static void each_group(void)
|
1037
|
+
{
|
1038
|
+
if (grp_block)
|
1039
|
+
rb_raise(rb_eRuntimeError, "parallel group iteration");
|
1040
|
+
grp_block = (int)Qtrue;
|
1041
|
+
rb_ensure(grp_iterate, 0, grp_ensure, 0);
|
1042
|
+
}
|
1043
|
+
|
1044
|
+
VALUE eu_getgrent(VALUE self)
|
1045
|
+
{
|
1046
|
+
struct group *grp;
|
1047
|
+
|
1048
|
+
if (rb_block_given_p())
|
1049
|
+
each_group();
|
1050
|
+
else if ( (grp = getgrent()) )
|
1051
|
+
return setup_group(grp);
|
1052
|
+
return Qnil;
|
1053
|
+
}
|
1054
|
+
#endif
|
1055
|
+
|
1056
|
+
#ifdef GSHADOW
|
1057
|
+
static int sgrp_block = 0;
|
1058
|
+
|
1059
|
+
static VALUE sgrp_iterate(void)
|
1060
|
+
{
|
1061
|
+
struct sgrp *sgroup;
|
1062
|
+
|
1063
|
+
setsgent();
|
1064
|
+
while ( (sgroup = getsgent()) )
|
1065
|
+
rb_yield(setup_gshadow(sgroup));
|
1066
|
+
return Qnil;
|
1067
|
+
}
|
1068
|
+
|
1069
|
+
static VALUE sgrp_ensure(void)
|
1070
|
+
{
|
1071
|
+
#ifdef HAVE_ENDSGENT
|
1072
|
+
endsgent();
|
1073
|
+
sgrp_block = (int)Qfalse;
|
1074
|
+
#endif
|
1075
|
+
return Qnil;
|
1076
|
+
}
|
1077
|
+
|
1078
|
+
static void each_sgrp(void)
|
1079
|
+
{
|
1080
|
+
if (sgrp_block)
|
1081
|
+
rb_raise(rb_eRuntimeError, "parallel gshadow iteration");
|
1082
|
+
sgrp_block = (int)Qtrue;
|
1083
|
+
rb_ensure(sgrp_iterate, 0, sgrp_ensure, 0);
|
1084
|
+
}
|
1085
|
+
|
1086
|
+
VALUE eu_getsgent(VALUE self)
|
1087
|
+
{
|
1088
|
+
struct sgrp *sgroup;
|
1089
|
+
|
1090
|
+
if (rb_block_given_p())
|
1091
|
+
each_sgrp();
|
1092
|
+
else if ( (sgroup = getsgent()) )
|
1093
|
+
return setup_gshadow(sgroup);
|
1094
|
+
return Qnil;
|
1095
|
+
}
|
1096
|
+
#endif
|
1097
|
+
|
1098
|
+
VALUE eu_to_entry(VALUE self, VALUE(*user_to)(VALUE, VALUE))
|
1099
|
+
{
|
1100
|
+
size_t ln;
|
1101
|
+
VALUE line, io;
|
1102
|
+
char filename[] = "/tmp/etc_utilsXXXXXX";
|
1103
|
+
int fd = mkstemp(filename);
|
1104
|
+
|
1105
|
+
if ( fd == -1 )
|
1106
|
+
rb_raise(rb_eIOError,
|
1107
|
+
"Error creating temp file: %s", strerror(errno));
|
1108
|
+
|
1109
|
+
io = rb_file_open(filename,"w+");
|
1110
|
+
|
1111
|
+
line = user_to(self, io);
|
1112
|
+
if (!rb_obj_is_kind_of(line, rb_cString)) {
|
1113
|
+
rewind(RFILE_FPTR(io));
|
1114
|
+
line = rb_io_gets(io);
|
1115
|
+
}
|
1116
|
+
|
1117
|
+
if ( close(fd) < 0)
|
1118
|
+
rb_raise(rb_eIOError, "Error closing temp file: %s", strerror(errno));
|
1119
|
+
|
1120
|
+
rb_io_close(io);
|
1121
|
+
|
1122
|
+
if ( unlink(filename) < 0 )
|
1123
|
+
rb_raise(rb_eIOError, "Error unlinking temp file: %s", strerror(errno));
|
1124
|
+
|
1125
|
+
if (NIL_P(line))
|
1126
|
+
return Qnil;
|
1127
|
+
|
1128
|
+
ln = RSTRING_LEN(line);
|
1129
|
+
|
1130
|
+
if (RSTRING_PTR(line)[ln-1] == '\n')
|
1131
|
+
rb_str_resize(line, ln-1);
|
1132
|
+
|
1133
|
+
return line;
|
1134
|
+
}
|
1135
|
+
|
1136
|
+
static VALUE
|
1137
|
+
eu_setXXent(VALUE self)
|
1138
|
+
{
|
1139
|
+
eu_setpwent(self);
|
1140
|
+
eu_setgrent(self);
|
1141
|
+
eu_setspent(self);
|
1142
|
+
eu_setsgent(self);
|
1143
|
+
return Qnil;
|
1144
|
+
}
|
1145
|
+
|
1146
|
+
static VALUE
|
1147
|
+
eu_endXXent(VALUE self)
|
1148
|
+
{
|
1149
|
+
eu_endpwent(self);
|
1150
|
+
eu_endgrent(self);
|
1151
|
+
eu_endspent(self);
|
1152
|
+
eu_endsgent(self);
|
1153
|
+
return Qnil;
|
1154
|
+
}
|
1155
|
+
|
1156
|
+
static VALUE
|
1157
|
+
eu_getlogin(VALUE self)
|
1158
|
+
{
|
1159
|
+
struct passwd *pwd;
|
1160
|
+
|
1161
|
+
pwd = getpwuid(geteuid());
|
1162
|
+
return setup_passwd(pwd);
|
1163
|
+
}
|
1164
|
+
|
1165
|
+
static VALUE
|
1166
|
+
eu_passwd_p(VALUE self)
|
1167
|
+
{
|
1168
|
+
#ifdef PASSWD
|
1169
|
+
return Qtrue;
|
1170
|
+
#else
|
1171
|
+
return Qfalse;
|
1172
|
+
#endif
|
1173
|
+
}
|
1174
|
+
|
1175
|
+
static int
|
1176
|
+
eu_file_readable_p(const char *fname)
|
1177
|
+
{
|
1178
|
+
if (eaccess(fname, R_OK) < 0) return Qfalse;
|
1179
|
+
return Qtrue;
|
1180
|
+
}
|
1181
|
+
|
1182
|
+
static VALUE
|
1183
|
+
eu_read_passwd_p(VALUE self)
|
1184
|
+
{
|
1185
|
+
if (eu_passwd_p(self))
|
1186
|
+
return eu_file_readable_p(PASSWD);
|
1187
|
+
return Qfalse;
|
1188
|
+
}
|
1189
|
+
|
1190
|
+
static VALUE
|
1191
|
+
eu_shadow_p(VALUE self)
|
1192
|
+
{
|
1193
|
+
#ifdef SHADOW
|
1194
|
+
return Qtrue;
|
1195
|
+
#else
|
1196
|
+
return Qfalse;
|
1197
|
+
#endif
|
1198
|
+
}
|
1199
|
+
|
1200
|
+
static VALUE
|
1201
|
+
eu_read_shadow_p(VALUE self)
|
1202
|
+
{
|
1203
|
+
#ifdef SHADOW
|
1204
|
+
if (eu_shadow_p(self))
|
1205
|
+
return eu_file_readable_p(SHADOW);
|
1206
|
+
#endif
|
1207
|
+
return Qfalse;
|
1208
|
+
}
|
1209
|
+
|
1210
|
+
static VALUE
|
1211
|
+
eu_group_p(VALUE self)
|
1212
|
+
{
|
1213
|
+
#ifdef GROUP
|
1214
|
+
return Qtrue;
|
1215
|
+
#else
|
1216
|
+
return Qfalse;
|
1217
|
+
#endif
|
1218
|
+
}
|
1219
|
+
|
1220
|
+
static VALUE
|
1221
|
+
eu_read_group_p(VALUE self)
|
1222
|
+
{
|
1223
|
+
if (eu_group_p(self))
|
1224
|
+
return eu_file_readable_p(GROUP);
|
1225
|
+
return Qfalse;
|
1226
|
+
}
|
1227
|
+
|
1228
|
+
static VALUE
|
1229
|
+
eu_gshadow_p(VALUE self)
|
1230
|
+
{
|
1231
|
+
#ifdef GSHADOW
|
1232
|
+
return Qtrue;
|
1233
|
+
#else
|
1234
|
+
return Qfalse;
|
1235
|
+
#endif
|
1236
|
+
}
|
1237
|
+
|
1238
|
+
static VALUE
|
1239
|
+
eu_read_gshadow_p(VALUE self)
|
1240
|
+
{
|
1241
|
+
#ifdef GSHADOW
|
1242
|
+
if (eu_gshadow_p(self) && eu_file_readable_p(GSHADOW))
|
1243
|
+
if (getsgent()) {
|
1244
|
+
setsgent();
|
1245
|
+
return Qtrue;
|
1246
|
+
}
|
1247
|
+
#endif
|
1248
|
+
return Qfalse;
|
1249
|
+
}
|
1250
|
+
|
1251
|
+
static VALUE
|
1252
|
+
eu_lockable_p(VALUE self)
|
1253
|
+
{
|
1254
|
+
#if defined(HAVE_LCKPWDF) ||defined(HAVE_ULCKPWDF)
|
1255
|
+
return Qtrue;
|
1256
|
+
#else
|
1257
|
+
return Qfalse;
|
1258
|
+
#endif
|
1259
|
+
}
|
1260
|
+
|
1261
|
+
void Init_etcutils()
|
1262
|
+
{
|
1263
|
+
mEtcUtils = rb_define_module("EtcUtils");
|
1264
|
+
|
1265
|
+
assigned_uids = rb_ary_new();
|
1266
|
+
assigned_gids = rb_ary_new();
|
1267
|
+
uid_global = UINT2NUM((uid_t)0);
|
1268
|
+
gid_global = UINT2NUM((gid_t)0);
|
1269
|
+
|
1270
|
+
rb_global_variable(&assigned_uids);
|
1271
|
+
rb_global_variable(&assigned_gids);
|
1272
|
+
rb_global_variable(&uid_global);
|
1273
|
+
rb_global_variable(&gid_global);
|
1274
|
+
|
1275
|
+
rb_cPasswd = rb_define_class_under(mEtcUtils,"Passwd",rb_cObject);
|
1276
|
+
rb_extend_object(rb_cPasswd, rb_mEnumerable);
|
1277
|
+
|
1278
|
+
rb_cShadow = rb_define_class_under(mEtcUtils,"Shadow",rb_cObject);
|
1279
|
+
rb_extend_object(rb_cShadow, rb_mEnumerable);
|
1280
|
+
|
1281
|
+
rb_cGroup = rb_define_class_under(mEtcUtils,"Group",rb_cObject);
|
1282
|
+
rb_extend_object(rb_cGroup, rb_mEnumerable);
|
1283
|
+
|
1284
|
+
rb_cGshadow = rb_define_class_under(mEtcUtils,"GShadow",rb_cObject);
|
1285
|
+
rb_extend_object(rb_cGshadow, rb_mEnumerable);
|
1286
|
+
rb_define_const(mEtcUtils, "Gshadow", rb_cGshadow);
|
1287
|
+
|
1288
|
+
id_name = rb_intern("@name");
|
1289
|
+
id_passwd = rb_intern("@passwd");
|
1290
|
+
id_uid = rb_intern("@uid");
|
1291
|
+
id_gid = rb_intern("@gid");
|
1292
|
+
|
1293
|
+
/* EtcUtils Constants */
|
1294
|
+
#ifdef PASSWD
|
1295
|
+
rb_define_const(mEtcUtils, "PASSWD", setup_safe_str(PASSWD));
|
1296
|
+
#endif
|
1297
|
+
#ifdef SHADOW
|
1298
|
+
rb_define_const(mEtcUtils, "SHADOW", setup_safe_str(SHADOW));
|
1299
|
+
#endif
|
1300
|
+
#ifdef GROUP
|
1301
|
+
rb_define_const(mEtcUtils, "GROUP", setup_safe_str(GROUP));
|
1302
|
+
#endif
|
1303
|
+
#ifdef GSHADOW
|
1304
|
+
rb_define_const(mEtcUtils, "GSHADOW", setup_safe_str(GSHADOW));
|
1305
|
+
#endif
|
1306
|
+
rb_define_const(mEtcUtils, "SHELL", setup_safe_str(DEFAULT_SHELL));
|
1307
|
+
|
1308
|
+
/* EtcUtils Reflective Functions */
|
1309
|
+
rb_define_module_function(mEtcUtils,"me",eu_getlogin,0);
|
1310
|
+
rb_define_module_function(mEtcUtils,"getlogin",eu_getlogin,0);
|
1311
|
+
/* EtcUtils Truthy Functions */
|
1312
|
+
rb_define_module_function(mEtcUtils,"has_passwd?",eu_passwd_p,0);
|
1313
|
+
rb_define_module_function(mEtcUtils,"read_passwd?",eu_read_passwd_p,0);
|
1314
|
+
rb_define_module_function(mEtcUtils,"has_shadow?",eu_shadow_p,0);
|
1315
|
+
rb_define_module_function(mEtcUtils,"read_shadow?",eu_read_shadow_p,0);
|
1316
|
+
rb_define_module_function(mEtcUtils,"has_group?",eu_group_p,0);
|
1317
|
+
rb_define_module_function(mEtcUtils,"read_group?",eu_read_group_p,0);
|
1318
|
+
rb_define_module_function(mEtcUtils,"has_gshadow?",eu_gshadow_p,0);
|
1319
|
+
rb_define_module_function(mEtcUtils,"read_gshadow?",eu_read_gshadow_p,0);
|
1320
|
+
rb_define_module_function(mEtcUtils,"can_lockfile?",eu_lockable_p,0);
|
1321
|
+
/* EtcUtils Module functions */
|
1322
|
+
rb_define_module_function(mEtcUtils,"next_uid",next_uid,-1);
|
1323
|
+
rb_define_module_function(mEtcUtils,"next_gid",next_gid,-1);
|
1324
|
+
rb_define_module_function(mEtcUtils,"next_uid=",next_uid,-1);
|
1325
|
+
rb_define_module_function(mEtcUtils,"next_gid=",next_gid,-1);
|
1326
|
+
rb_define_module_function(mEtcUtils,"setXXent",eu_setXXent,0);
|
1327
|
+
rb_define_module_function(mEtcUtils,"endXXent",eu_endXXent,0);
|
1328
|
+
/* EtcUtils Lock Functions */
|
1329
|
+
#ifdef HAVE_LCKPWDF
|
1330
|
+
rb_define_module_function(mEtcUtils,"lckpwdf",eu_lckpwdf,0);
|
1331
|
+
rb_define_module_function(mEtcUtils,"lock",eu_lock,0);
|
1332
|
+
rb_define_module_function(mEtcUtils,"locked?",eu_locked_p,0);
|
1333
|
+
#endif
|
1334
|
+
#ifdef HAVE_ULCKPWDF
|
1335
|
+
rb_define_module_function(mEtcUtils,"ulckpwdf",eu_ulckpwdf,0);
|
1336
|
+
rb_define_module_function(mEtcUtils,"unlock",eu_unlock,0);
|
1337
|
+
#endif
|
1338
|
+
|
1339
|
+
|
1340
|
+
#ifdef SHADOW
|
1341
|
+
/* EU::Shadow module helpers */
|
1342
|
+
rb_define_module_function(mEtcUtils,"getspent",eu_getspent,0);
|
1343
|
+
rb_define_module_function(mEtcUtils,"find_spwd",eu_getspwd,1);
|
1344
|
+
rb_define_module_function(mEtcUtils,"setspent",eu_setspent,0);
|
1345
|
+
rb_define_module_function(mEtcUtils,"endspent",eu_endspent,0);
|
1346
|
+
rb_define_module_function(mEtcUtils,"sgetspent",eu_sgetspent,1);
|
1347
|
+
rb_define_module_function(mEtcUtils,"fgetspent",eu_fgetspent,1);
|
1348
|
+
rb_define_module_function(mEtcUtils,"putspent",eu_putspent,2);
|
1349
|
+
/* Backward compatibility */
|
1350
|
+
rb_define_module_function(mEtcUtils, "getspnam",eu_getspwd,1);
|
1351
|
+
#endif
|
1352
|
+
|
1353
|
+
#ifdef PASSWD
|
1354
|
+
/* EU::Passwd module helpers */
|
1355
|
+
rb_define_module_function(mEtcUtils,"getpwent",eu_getpwent,0);
|
1356
|
+
rb_define_module_function(mEtcUtils,"find_pwd",eu_getpwd,1);
|
1357
|
+
rb_define_module_function(mEtcUtils,"setpwent",eu_setpwent,0);
|
1358
|
+
rb_define_module_function(mEtcUtils,"endpwent",eu_endpwent,0);
|
1359
|
+
rb_define_module_function(mEtcUtils,"sgetpwent",eu_sgetpwent,1);
|
1360
|
+
#ifdef HAVE_FGETPWENT
|
1361
|
+
rb_define_module_function(mEtcUtils,"fgetpwent",eu_fgetpwent,1);
|
1362
|
+
#endif
|
1363
|
+
rb_define_module_function(mEtcUtils,"putpwent",eu_putpwent,2);
|
1364
|
+
/* Backward compatibility */
|
1365
|
+
rb_define_module_function(mEtcUtils,"getpwnam",eu_getpwd,1);
|
1366
|
+
#endif
|
1367
|
+
|
1368
|
+
#ifdef GSHADOW
|
1369
|
+
/* EU::GShadow module helpers */
|
1370
|
+
rb_define_module_function(mEtcUtils,"getsgent",eu_getsgent,0);
|
1371
|
+
rb_define_module_function(mEtcUtils,"find_sgrp",eu_getsgrp,1);
|
1372
|
+
rb_define_module_function(mEtcUtils,"setsgent",eu_setsgent,0);
|
1373
|
+
rb_define_module_function(mEtcUtils,"endsgent",eu_endsgent,0);
|
1374
|
+
rb_define_module_function(mEtcUtils,"sgetsgent",eu_sgetsgent,1);
|
1375
|
+
rb_define_module_function(mEtcUtils,"fgetsgent",eu_fgetsgent,1);
|
1376
|
+
rb_define_module_function(mEtcUtils,"putsgent",eu_putsgent,2);
|
1377
|
+
/* Backward compatibility */
|
1378
|
+
rb_define_module_function(mEtcUtils,"getsgnam",eu_getsgrp,1);
|
1379
|
+
#endif
|
1380
|
+
|
1381
|
+
#ifdef GROUP
|
1382
|
+
/* EU::Group module helpers */
|
1383
|
+
rb_define_module_function(mEtcUtils,"getgrent",eu_getgrent,0);
|
1384
|
+
rb_define_module_function(mEtcUtils,"find_grp",eu_getgrp,1);
|
1385
|
+
rb_define_module_function(mEtcUtils,"setgrent",eu_setgrent,0);
|
1386
|
+
rb_define_module_function(mEtcUtils,"endgrent",eu_endgrent,0);
|
1387
|
+
rb_define_module_function(mEtcUtils,"sgetgrent",eu_sgetgrent,1);
|
1388
|
+
#ifdef HAVE_FGETGRENT
|
1389
|
+
rb_define_module_function(mEtcUtils,"fgetgrent",eu_fgetgrent,1);
|
1390
|
+
#endif
|
1391
|
+
rb_define_module_function(mEtcUtils,"putgrent",eu_putgrent,2);
|
1392
|
+
/* Backward compatibility */
|
1393
|
+
rb_define_module_function(mEtcUtils,"getgrnam",eu_getgrp,1);
|
1394
|
+
#endif
|
1395
|
+
|
1396
|
+
Init_etcutils_user();
|
1397
|
+
Init_etcutils_group();
|
1398
|
+
}
|