puppet 7.0.0 → 7.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puppet might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CODEOWNERS +2 -16
- data/Gemfile +2 -3
- data/Gemfile.lock +13 -9
- data/ext/project_data.yaml +1 -0
- data/lib/puppet/application_support.rb +7 -0
- data/lib/puppet/defaults.rb +1 -27
- data/lib/puppet/environments.rb +38 -54
- data/lib/puppet/face/node/clean.rb +8 -0
- data/lib/puppet/ffi/posix.rb +10 -0
- data/lib/puppet/ffi/posix/constants.rb +14 -0
- data/lib/puppet/ffi/posix/functions.rb +24 -0
- data/lib/puppet/parser/templatewrapper.rb +1 -1
- data/lib/puppet/provider/user/aix.rb +2 -2
- data/lib/puppet/type/user.rb +1 -1
- data/lib/puppet/util/posix.rb +53 -4
- data/lib/puppet/version.rb +1 -1
- data/man/man5/puppet.conf.5 +2 -2
- data/man/man8/puppet-agent.8 +1 -1
- data/man/man8/puppet-apply.8 +1 -1
- data/man/man8/puppet-catalog.8 +1 -1
- data/man/man8/puppet-config.8 +1 -1
- data/man/man8/puppet-describe.8 +1 -1
- data/man/man8/puppet-device.8 +1 -1
- data/man/man8/puppet-doc.8 +1 -1
- data/man/man8/puppet-epp.8 +1 -1
- data/man/man8/puppet-facts.8 +1 -1
- data/man/man8/puppet-filebucket.8 +1 -1
- data/man/man8/puppet-generate.8 +1 -1
- data/man/man8/puppet-help.8 +1 -1
- data/man/man8/puppet-lookup.8 +1 -1
- data/man/man8/puppet-module.8 +1 -1
- data/man/man8/puppet-node.8 +1 -1
- data/man/man8/puppet-parser.8 +1 -1
- data/man/man8/puppet-plugin.8 +1 -1
- data/man/man8/puppet-report.8 +1 -1
- data/man/man8/puppet-resource.8 +1 -1
- data/man/man8/puppet-script.8 +1 -1
- data/man/man8/puppet-ssl.8 +1 -1
- data/man/man8/puppet.8 +2 -2
- data/spec/fixtures/unit/provider/user/aix/aix_passwd_file.out +4 -0
- data/spec/unit/application_spec.rb +34 -0
- data/spec/unit/defaults_spec.rb +1 -56
- data/spec/unit/environments_spec.rb +96 -19
- data/spec/unit/face/node_spec.rb +14 -2
- data/spec/unit/parser/templatewrapper_spec.rb +4 -3
- data/spec/unit/provider/user/aix_spec.rb +5 -0
- data/spec/unit/provider/user/pw_spec.rb +2 -0
- data/spec/unit/provider/user/useradd_spec.rb +1 -0
- data/spec/unit/util/posix_spec.rb +357 -15
- data/spec/unit/util/storage_spec.rb +3 -1
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a266286fc66516f823076b6e02e50940b8c31a5d2a15e671b4e3d0109d1037a8
|
4
|
+
data.tar.gz: fb8bfad528606af04781e2119331ff44b871e18174b655f47a2d0ec31b7dea85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 32b3341aec92b3a0147bbc5ac67fd043d5121670add0955c1c38765d998c6cb327660031152df284bd75e7fd4579c2536da581979fb6f08b3c1080e730a38a78
|
7
|
+
data.tar.gz: a8ed38a33320b7889eef178539be960b1ceb171f4d4c94760c036ea10bda609f4ac47c0438503a260af2a03def16faa283f9be41048bfbcfafbd677931010509
|
data/CODEOWNERS
CHANGED
@@ -1,23 +1,9 @@
|
|
1
|
-
#
|
2
|
-
* @puppetlabs/platform-core @puppetlabs/puppetserver-maintainers
|
3
|
-
|
4
|
-
# Night's Watch
|
5
|
-
/lib/puppet/type/group @puppetlabs/night-s-watch
|
6
|
-
/lib/puppet/type/package @puppetlabs/night-s-watch
|
7
|
-
/lib/puppet/type/service @puppetlabs/night-s-watch
|
8
|
-
/lib/puppet/type/user @puppetlabs/night-s-watch
|
9
|
-
/lib/puppet/provider/group @puppetlabs/night-s-watch
|
10
|
-
/lib/puppet/provider/package @puppetlabs/night-s-watch
|
11
|
-
/lib/puppet/provider/service @puppetlabs/night-s-watch
|
12
|
-
/lib/puppet/provider/user @puppetlabs/night-s-watch
|
1
|
+
# defaults
|
2
|
+
* @puppetlabs/platform-core @puppetlabs/puppetserver-maintainers @puppetlabs/night-s-watch
|
13
3
|
|
14
4
|
# PAL
|
15
5
|
/lib/puppet/pal @puppetlabs/bolt
|
16
6
|
|
17
|
-
# puppet device
|
18
|
-
/lib/puppet/application/device.rb @puppetlabs/networking
|
19
|
-
/lib/puppet/util/network_device @puppetlabs/networking
|
20
|
-
|
21
7
|
# puppet module
|
22
8
|
/lib/puppet/application/module.rb @puppetlabs/pdk
|
23
9
|
/lib/puppet/face/module @puppetlabs/pdk
|
data/Gemfile
CHANGED
@@ -18,8 +18,6 @@ gem "hiera", *location_for(ENV['HIERA_LOCATION']) if ENV.has_key?('HIERA_LOCATIO
|
|
18
18
|
gem "semantic_puppet", *location_for(ENV['SEMANTIC_PUPPET_LOCATION'] || ["~> 1.0"])
|
19
19
|
gem "puppet-resource_api", *location_for(ENV['RESOURCE_API_LOCATION'] || ["~> 1.5"])
|
20
20
|
|
21
|
-
gem "scanf" if RUBY_VERSION.to_f >= 2.7
|
22
|
-
|
23
21
|
group(:features) do
|
24
22
|
gem 'diff-lcs', '~> 1.3', require: false
|
25
23
|
gem 'hiera-eyaml', *location_for(ENV['HIERA_EYAML_LOCATION'])
|
@@ -33,10 +31,11 @@ group(:features) do
|
|
33
31
|
# gem 'ruby-augeas', require: false, platforms: [:ruby]
|
34
32
|
# requires native ldap headers/libs
|
35
33
|
# gem 'ruby-ldap', '~> 0.9', require: false, platforms: [:ruby]
|
36
|
-
gem 'puppetserver-ca', '~>
|
34
|
+
gem 'puppetserver-ca', '~> 2.0', require: false
|
37
35
|
end
|
38
36
|
|
39
37
|
group(:test) do
|
38
|
+
gem "ffi", require: false
|
40
39
|
gem "json-schema", "~> 2.0", require: false
|
41
40
|
gem "rake", *location_for(ENV['RAKE_LOCATION'] || '~> 12.2')
|
42
41
|
gem "rspec", "~> 3.1", require: false
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
puppet (7.
|
4
|
+
puppet (7.1.0)
|
5
5
|
CFPropertyList (~> 2.2)
|
6
6
|
concurrent-ruby (~> 1.0)
|
7
7
|
deep_merge (~> 1.0)
|
@@ -10,6 +10,7 @@ PATH
|
|
10
10
|
hiera (>= 3.2.1, < 4)
|
11
11
|
locale (~> 2.1)
|
12
12
|
multi_json (~> 1.13)
|
13
|
+
scanf (~> 1.0)
|
13
14
|
semantic_puppet (~> 1.0)
|
14
15
|
|
15
16
|
GEM
|
@@ -27,10 +28,11 @@ GEM
|
|
27
28
|
deep_merge (1.2.1)
|
28
29
|
diff-lcs (1.4.4)
|
29
30
|
docopt (0.6.1)
|
30
|
-
facter (4.0.
|
31
|
+
facter (4.0.46)
|
31
32
|
hocon (~> 1.3)
|
32
33
|
thor (>= 1.0.1, < 2.0)
|
33
34
|
fast_gettext (1.1.2)
|
35
|
+
ffi (1.13.1)
|
34
36
|
gettext (3.2.9)
|
35
37
|
locale (>= 2.0.5)
|
36
38
|
text (>= 1.3.0)
|
@@ -49,29 +51,29 @@ GEM
|
|
49
51
|
json-schema (2.8.1)
|
50
52
|
addressable (>= 2.4)
|
51
53
|
locale (2.1.3)
|
52
|
-
memory_profiler (0.
|
54
|
+
memory_profiler (1.0.0)
|
53
55
|
method_source (1.0.0)
|
54
56
|
minitar (0.9)
|
55
57
|
msgpack (1.3.3)
|
56
58
|
multi_json (1.15.0)
|
57
59
|
mustache (1.1.1)
|
58
60
|
optimist (3.0.1)
|
59
|
-
packaging (0.99.
|
61
|
+
packaging (0.99.75)
|
60
62
|
artifactory (~> 2)
|
61
63
|
csv (= 3.1.5)
|
62
64
|
rake (>= 12.3)
|
63
65
|
release-metrics
|
64
|
-
parallel (1.20.
|
66
|
+
parallel (1.20.1)
|
65
67
|
parser (2.7.2.0)
|
66
68
|
ast (~> 2.4.1)
|
67
|
-
powerpack (0.1.
|
69
|
+
powerpack (0.1.3)
|
68
70
|
pry (0.13.1)
|
69
71
|
coderay (~> 1.1)
|
70
72
|
method_source (~> 1.0)
|
71
73
|
public_suffix (4.0.6)
|
72
74
|
puppet-resource_api (1.8.13)
|
73
75
|
hocon (>= 1.0)
|
74
|
-
puppetserver-ca (
|
76
|
+
puppetserver-ca (2.0.1)
|
75
77
|
facter (>= 2.0.1, < 5)
|
76
78
|
racc (1.4.9)
|
77
79
|
rainbow (2.2.2)
|
@@ -113,6 +115,7 @@ GEM
|
|
113
115
|
rubocop (~> 0.49.0)
|
114
116
|
ruby-prof (1.4.2)
|
115
117
|
ruby-progressbar (1.10.1)
|
118
|
+
scanf (1.0.0)
|
116
119
|
semantic_puppet (1.0.2)
|
117
120
|
text (1.3.1)
|
118
121
|
thor (1.0.1)
|
@@ -129,6 +132,7 @@ PLATFORMS
|
|
129
132
|
|
130
133
|
DEPENDENCIES
|
131
134
|
diff-lcs (~> 1.3)
|
135
|
+
ffi
|
132
136
|
gettext-setup (~> 0.28)
|
133
137
|
hiera-eyaml
|
134
138
|
hocon (~> 1.0)
|
@@ -140,7 +144,7 @@ DEPENDENCIES
|
|
140
144
|
pry
|
141
145
|
puppet!
|
142
146
|
puppet-resource_api (~> 1.5)
|
143
|
-
puppetserver-ca (~>
|
147
|
+
puppetserver-ca (~> 2.0)
|
144
148
|
racc (= 1.4.9)
|
145
149
|
rake (~> 12.2)
|
146
150
|
rdoc (~> 6.0)
|
@@ -157,4 +161,4 @@ DEPENDENCIES
|
|
157
161
|
yard
|
158
162
|
|
159
163
|
BUNDLED WITH
|
160
|
-
|
164
|
+
1.17.3
|
data/ext/project_data.yaml
CHANGED
@@ -53,6 +53,13 @@ module Puppet
|
|
53
53
|
route_file = Puppet[:route_file]
|
54
54
|
if Puppet::FileSystem.exist?(route_file)
|
55
55
|
routes = Puppet::Util::Yaml.safe_load_file(route_file, [Symbol])
|
56
|
+
if routes["server"] && routes["master"]
|
57
|
+
Puppet.warning("Route file #{route_file} contains both server and master route settings.")
|
58
|
+
elsif routes["server"] && !routes["master"]
|
59
|
+
routes["master"] = routes["server"]
|
60
|
+
elsif routes["master"] && !routes["server"]
|
61
|
+
routes["server"] = routes["master"]
|
62
|
+
end
|
56
63
|
application_routes = routes[application_name]
|
57
64
|
Puppet::Indirector.configure_routes(application_routes) if application_routes
|
58
65
|
end
|
data/lib/puppet/defaults.rb
CHANGED
@@ -32,20 +32,6 @@ module Puppet
|
|
32
32
|
%w[sha256 sha256lite sha384 sha512 sha224 sha1 sha1lite md5 md5lite mtime ctime]
|
33
33
|
end
|
34
34
|
|
35
|
-
def self.log_ca_migration_warning
|
36
|
-
urge_to_migrate = <<-UTM
|
37
|
-
The cadir is currently configured to be inside the #{Puppet[:ssldir]} directory. This config
|
38
|
-
setting and the directory location will not be used in a future version of puppet. Please run the
|
39
|
-
puppetserver ca tool to migrate out from the puppet confdir to the /etc/puppetlabs/puppetserver/ca
|
40
|
-
directory. Use `puppetserver ca migrate --help` for more info.
|
41
|
-
UTM
|
42
|
-
Puppet.warn_once('deprecations',
|
43
|
-
'CA migration message',
|
44
|
-
urge_to_migrate,
|
45
|
-
:default,
|
46
|
-
:default)
|
47
|
-
end
|
48
|
-
|
49
35
|
def self.default_cadir
|
50
36
|
return "" if Puppet::Util::Platform.windows?
|
51
37
|
old_ca_dir = "#{Puppet[:ssldir]}/ca"
|
@@ -53,13 +39,8 @@ UTM
|
|
53
39
|
|
54
40
|
if File.exist?(old_ca_dir)
|
55
41
|
if File.symlink?(old_ca_dir)
|
56
|
-
|
57
|
-
if target.start_with?(Puppet[:ssldir])
|
58
|
-
Puppet.log_ca_migration_warning
|
59
|
-
end
|
60
|
-
target
|
42
|
+
File.readlink(old_ca_dir)
|
61
43
|
else
|
62
|
-
Puppet.log_ca_migration_warning
|
63
44
|
old_ca_dir
|
64
45
|
end
|
65
46
|
else
|
@@ -1112,13 +1093,6 @@ EOT
|
|
1112
1093
|
:default => lambda { default_cadir },
|
1113
1094
|
:type => :directory,
|
1114
1095
|
:desc => "The root directory for the certificate authority.",
|
1115
|
-
:call_hook => :on_initialize_and_write,
|
1116
|
-
:hook => proc do |value|
|
1117
|
-
if value.start_with?(Puppet[:ssldir])
|
1118
|
-
Puppet.log_ca_migration_warning
|
1119
|
-
end
|
1120
|
-
value
|
1121
|
-
end
|
1122
1096
|
},
|
1123
1097
|
:cacert => {
|
1124
1098
|
:default => "$cadir/ca_crt.pem",
|
data/lib/puppet/environments.rb
CHANGED
@@ -346,12 +346,6 @@ module Puppet::Environments
|
|
346
346
|
@loader = loader
|
347
347
|
@cache_expiration_service = Puppet::Environments::Cached.cache_expiration_service
|
348
348
|
@cache = {}
|
349
|
-
|
350
|
-
# Holds expiration times in sorted order - next to expire is first
|
351
|
-
@expirations = SortedSet.new
|
352
|
-
|
353
|
-
# Infinity since it there are no entries, this is a cache of the first to expire time
|
354
|
-
@next_expiration = END_OF_TIME
|
355
349
|
end
|
356
350
|
|
357
351
|
# @!macro loader_list
|
@@ -379,7 +373,6 @@ module Puppet::Environments
|
|
379
373
|
elsif (result = @loader.get(name))
|
380
374
|
# environment loaded, cache it
|
381
375
|
cache_entry = entry(result)
|
382
|
-
@cache_expiration_service.created(result)
|
383
376
|
add_entry(name, cache_entry)
|
384
377
|
result
|
385
378
|
end
|
@@ -389,28 +382,36 @@ module Puppet::Environments
|
|
389
382
|
def add_entry(name, cache_entry)
|
390
383
|
Puppet.debug {"Caching environment '#{name}' #{cache_entry.label}"}
|
391
384
|
@cache[name] = cache_entry
|
392
|
-
|
393
|
-
@expirations.add(expires)
|
394
|
-
if @next_expiration > expires
|
395
|
-
@next_expiration = expires
|
396
|
-
end
|
385
|
+
@cache_expiration_service.created(cache_entry.value)
|
397
386
|
end
|
398
387
|
private :add_entry
|
399
388
|
|
389
|
+
def clear_entry(name, entry)
|
390
|
+
@cache.delete(name)
|
391
|
+
Puppet.debug {"Evicting cache entry for environment '#{name}'"}
|
392
|
+
@cache_expiration_service.evicted(name.to_sym)
|
393
|
+
Puppet::GettextConfig.delete_text_domain(name)
|
394
|
+
Puppet.settings.clear_environment_settings(name)
|
395
|
+
end
|
396
|
+
private :clear_entry
|
397
|
+
|
400
398
|
# Clears the cache of the environment with the given name.
|
401
399
|
# (The intention is that this could be used from a MANUAL cache eviction command (TBD)
|
402
400
|
def clear(name)
|
403
|
-
@cache
|
404
|
-
|
401
|
+
entry = @cache[name]
|
402
|
+
clear_entry(name, entry) if entry
|
405
403
|
end
|
406
404
|
|
407
405
|
# Clears all cached environments.
|
408
406
|
# (The intention is that this could be used from a MANUAL cache eviction command (TBD)
|
409
|
-
def clear_all
|
407
|
+
def clear_all
|
410
408
|
super
|
409
|
+
|
410
|
+
@cache.each_pair do |name, entry|
|
411
|
+
clear_entry(name, entry)
|
412
|
+
end
|
413
|
+
|
411
414
|
@cache = {}
|
412
|
-
@expirations.clear
|
413
|
-
@next_expiration = END_OF_TIME
|
414
415
|
Puppet::GettextConfig.delete_environment_text_domains
|
415
416
|
end
|
416
417
|
|
@@ -419,18 +420,24 @@ module Puppet::Environments
|
|
419
420
|
#
|
420
421
|
def clear_all_expired()
|
421
422
|
t = Time.now
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
Puppet.debug {"Evicting cache entry for environment '#{name}'"}
|
426
|
-
@cache_expiration_service.evicted(name.to_sym)
|
427
|
-
clear(name)
|
428
|
-
@expirations.delete(entry.expires)
|
429
|
-
Puppet.settings.clear_environment_settings(name)
|
423
|
+
|
424
|
+
@cache.each_pair do |name, entry|
|
425
|
+
clear_if_expired(name, entry, t)
|
430
426
|
end
|
431
|
-
@next_expiration = @expirations.first || END_OF_TIME
|
432
427
|
end
|
433
428
|
|
429
|
+
# Clear an environment if it is expired, either by exceeding its time to live, or
|
430
|
+
# through an explicit eviction determined by the cache expiration service.
|
431
|
+
#
|
432
|
+
def clear_if_expired(name, entry, t = Time.now)
|
433
|
+
return unless entry
|
434
|
+
|
435
|
+
if entry.expired?(t) || @cache_expiration_service.expired?(name.to_sym)
|
436
|
+
clear_entry(name, entry)
|
437
|
+
end
|
438
|
+
end
|
439
|
+
private :clear_if_expired
|
440
|
+
|
434
441
|
# This implementation evicts the cache, and always gets the current
|
435
442
|
# configuration of the environment
|
436
443
|
#
|
@@ -440,7 +447,7 @@ module Puppet::Environments
|
|
440
447
|
#
|
441
448
|
# @!macro loader_get_conf
|
442
449
|
def get_conf(name)
|
443
|
-
|
450
|
+
clear_if_expired(name, @cache[name])
|
444
451
|
@loader.get_conf(name)
|
445
452
|
end
|
446
453
|
|
@@ -463,17 +470,6 @@ module Puppet::Environments
|
|
463
470
|
end
|
464
471
|
end
|
465
472
|
|
466
|
-
# Evicts the entry if it has expired
|
467
|
-
# Also clears caches in Settings that may prevent the entry from being updated
|
468
|
-
def evict_if_expired(name)
|
469
|
-
if (result = @cache[name]) && (result.expired? || @cache_expiration_service.expired?(name.to_sym))
|
470
|
-
Puppet.debug {"Evicting cache entry for environment '#{name}'"}
|
471
|
-
@cache_expiration_service.evicted(name.to_sym)
|
472
|
-
clear(name)
|
473
|
-
Puppet.settings.clear_environment_settings(name)
|
474
|
-
end
|
475
|
-
end
|
476
|
-
|
477
473
|
# Never evicting entry
|
478
474
|
class Entry
|
479
475
|
attr_reader :value
|
@@ -485,32 +481,24 @@ module Puppet::Environments
|
|
485
481
|
def touch
|
486
482
|
end
|
487
483
|
|
488
|
-
def expired?
|
484
|
+
def expired?(now)
|
489
485
|
false
|
490
486
|
end
|
491
487
|
|
492
488
|
def label
|
493
489
|
""
|
494
490
|
end
|
495
|
-
|
496
|
-
def expires
|
497
|
-
END_OF_TIME
|
498
|
-
end
|
499
491
|
end
|
500
492
|
|
501
493
|
# Always evicting entry
|
502
494
|
class NotCachedEntry < Entry
|
503
|
-
def expired?
|
495
|
+
def expired?(now)
|
504
496
|
true
|
505
497
|
end
|
506
498
|
|
507
499
|
def label
|
508
500
|
"(ttl = 0 sec)"
|
509
501
|
end
|
510
|
-
|
511
|
-
def expires
|
512
|
-
START_OF_TIME
|
513
|
-
end
|
514
502
|
end
|
515
503
|
|
516
504
|
# Policy that expires if it hasn't been touched within ttl_seconds
|
@@ -527,12 +515,8 @@ module Puppet::Environments
|
|
527
515
|
@ttl = Time.now + @ttl_seconds
|
528
516
|
end
|
529
517
|
|
530
|
-
def expired?
|
531
|
-
|
532
|
-
end
|
533
|
-
|
534
|
-
def expires
|
535
|
-
@ttl
|
518
|
+
def expired?(now)
|
519
|
+
now > @ttl
|
536
520
|
end
|
537
521
|
|
538
522
|
def label
|
@@ -47,6 +47,14 @@ Puppet::Face.define(:node, '0.0.1') do
|
|
47
47
|
end
|
48
48
|
|
49
49
|
class LoggerIO
|
50
|
+
def debug(message)
|
51
|
+
Puppet.debug(message)
|
52
|
+
end
|
53
|
+
|
54
|
+
def warn(message)
|
55
|
+
Puppet.warning(message)
|
56
|
+
end
|
57
|
+
|
50
58
|
def err(message)
|
51
59
|
Puppet.err(message) unless message =~ /^\s*Error:\s*/
|
52
60
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'puppet/ffi/posix'
|
2
|
+
|
3
|
+
module Puppet::FFI::POSIX
|
4
|
+
module Constants
|
5
|
+
extend FFI::Library
|
6
|
+
|
7
|
+
# Maximum number of supplementary groups (groups
|
8
|
+
# that a user can be in plus its primary group)
|
9
|
+
# (64 + 1 primary group)
|
10
|
+
# Chosen a reasonable middle number from the list
|
11
|
+
# https://www.j3e.de/ngroups.html
|
12
|
+
MAXIMUM_NUMBER_OF_GROUPS = 65
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'puppet/ffi/posix'
|
2
|
+
|
3
|
+
module Puppet::FFI::POSIX
|
4
|
+
module Functions
|
5
|
+
|
6
|
+
extend FFI::Library
|
7
|
+
|
8
|
+
ffi_convention :stdcall
|
9
|
+
|
10
|
+
# https://man7.org/linux/man-pages/man3/getgrouplist.3.html
|
11
|
+
# int getgrouplist (
|
12
|
+
# const char *user,
|
13
|
+
# gid_t group,
|
14
|
+
# gid_t *groups,
|
15
|
+
# int *ngroups
|
16
|
+
# );
|
17
|
+
begin
|
18
|
+
ffi_lib FFI::Library::LIBC
|
19
|
+
attach_function :getgrouplist, [:string, :uint, :pointer, :pointer], :int
|
20
|
+
rescue FFI::NotFoundError
|
21
|
+
# Do nothing
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|