nsswrapper: implement group_del() in nss_wrapper.pl.
[ira/wip.git] / lib / nss_wrapper / nss_wrapper.pl
1 #!/usr/bin/perl
2 #
3
4 use strict;
5
6 use Getopt::Long;
7 use Cwd qw(abs_path);
8
9 my $opt_help = 0;
10 my $opt_path = undef;
11 my $opt_action = undef;
12 my $opt_type = undef;
13 my $opt_name = undef;
14
15 my $passwdfn = undef;
16 my $groupfn = undef;
17 my $actionfn = undef;
18
19 sub passwd_add($$);
20 sub passwd_delete($$);
21 sub group_add($$);
22 sub group_delete($$);
23
24 my $result = GetOptions(
25         'help|h|?'      => \$opt_help,
26         'path=s'        => \$opt_path,
27         'action=s'      => \$opt_action,
28         'type=s'        => \$opt_type,
29         'name=s'        => \$opt_name
30 );
31
32 sub usage($;$)
33 {
34         my ($ret, $msg) = @_;
35
36         print $msg."\n\n" if defined($msg);
37
38         print "usage:
39
40         --help|-h|-?            Show this help.
41
42         --path <path>           Path of the 'passwd' or 'group' file.
43
44         --type <type>           Only 'passwd' and 'group' are supported yet,
45                                 maybe 'member' will be added in future.
46
47         --action <action>       'add' or 'delete'.
48
49         --name <name>           The name of the object.
50 ";
51         exit($ret);
52 }
53
54 usage(1) if (not $result);
55
56 usage(0) if ($opt_help);
57
58 if (not defined($opt_path)) {
59         usage(1, "missing: --path <path>");
60 }
61 if ($opt_path eq "" or $opt_path eq "/") {
62         usage(1, "invalid: --path <path>: '$opt_path'");
63 }
64 my $opt_fullpath = abs_path($opt_path);
65 if (not defined($opt_fullpath)) {
66         usage(1, "invalid: --path <path>: '$opt_path'");
67 }
68
69
70 if (not defined($opt_action)) {
71         usage(1, "missing: --action [add|delete]");
72 }
73 if ($opt_action eq "add") {
74         $passwdfn = \&passwd_add;
75         $groupfn = \&group_add;
76 } elsif ($opt_action eq "delete") {
77         $passwdfn = \&passwd_delete;
78         $groupfn = \&group_delete;
79 } else {
80         usage(1, "invalid: --action [add|delete]: '$opt_action'");
81 }
82
83 if (not defined($opt_type)) {
84         usage(1, "missing: --type [passwd|group]");
85 }
86 if ($opt_type eq "passwd") {
87         $actionfn = $passwdfn;
88 } elsif ($opt_type eq "group") {
89         $actionfn = $groupfn;
90 } else {
91         usage(1, "invalid: --type [passwd|group]: '$opt_type'")
92 }
93
94 if (not defined($opt_name)) {
95         usage(1, "missing: --name <name>");
96 }
97 if ($opt_name eq "") {
98         usage(1, "invalid: --name <name>");
99 }
100
101 exit $actionfn->($opt_fullpath, $opt_name);
102
103 sub passwd_add_entry($$);
104
105 sub passwd_load($)
106 {
107         my ($path) = @_;
108         my @lines;
109         my $passwd = undef;
110
111         open(PWD, "<$path") or die("Unable to open '$path' for read");
112         @lines = <PWD>;
113         close(PWD);
114
115         $passwd->{array} = ();
116         $passwd->{name} = {};
117         $passwd->{uid} = {};
118         $passwd->{path} = $path;
119
120         foreach my $line (@lines) {
121                 passwd_add_entry($passwd, $line);
122         }
123
124         return $passwd;
125 }
126
127 sub group_add_entry($$);
128
129 sub group_load($)
130 {
131         my ($path) = @_;
132         my @lines;
133         my $group = undef;
134
135         open(GROUP, "<$path") or die("Unable to open '$path' for read");
136         @lines = <GROUP>;
137         close(GROUP);
138
139         $group->{array} = ();
140         $group->{name} = {};
141         $group->{gid} = {};
142         $group->{path} = $path;
143
144         foreach my $line (@lines) {
145                 group_add_entry($group, $line);
146         }
147
148         return $group;
149 }
150
151 sub passwd_lookup_name($$)
152 {
153         my ($passwd, $name) = @_;
154
155         return undef unless defined($passwd->{name}{$name});
156
157         return $passwd->{name}{$name};
158 }
159
160 sub group_lookup_name($$)
161 {
162         my ($group, $name) = @_;
163
164         return undef unless defined($group->{name}{$name});
165
166         return $group->{name}{$name};
167 }
168
169 sub passwd_lookup_uid($$)
170 {
171         my ($passwd, $uid) = @_;
172
173         return undef unless defined($passwd->{uid}{$uid});
174
175         return $passwd->{uid}{$uid};
176 }
177
178 sub group_lookup_gid($$)
179 {
180         my ($group, $gid) = @_;
181
182         return undef unless defined($group->{gid}{$gid});
183
184         return $group->{gid}{$gid};
185 }
186
187 sub passwd_get_free_uid($)
188 {
189         my ($passwd) = @_;
190         my $uid = 1000;
191
192         while (passwd_lookup_uid($passwd, $uid)) {
193                 $uid++;
194         }
195
196         return $uid;
197 }
198
199 sub group_get_free_gid($)
200 {
201         my ($group) = @_;
202         my $gid = 1000;
203
204         while (group_lookup_gid($group, $gid)) {
205                 $gid++;
206         }
207
208         return $gid;
209 }
210
211 sub passwd_add_entry($$)
212 {
213         my ($passwd, $str) = @_;
214
215         chomp $str;
216         my @e = split(':', $str);
217
218         push(@{$passwd->{array}}, \@e);
219         $passwd->{name}{$e[0]} = \@e;
220         $passwd->{uid}{$e[2]} = \@e;
221 }
222
223 sub group_add_entry($$)
224 {
225         my ($group, $str) = @_;
226
227         chomp $str;
228         my @e = split(':', $str);
229
230         push(@{$group->{array}}, \@e);
231         $group->{name}{$e[0]} = \@e;
232         $group->{gid}{$e[2]} = \@e;
233 }
234
235 sub passwd_remove_entry($$)
236 {
237         my ($passwd, $eref) = @_;
238
239         for (my $i = 0; defined($passwd->{array}[$i]); $i++) {
240                 if ($eref == $passwd->{array}[$i]) {
241                         $passwd->{array}[$i] = undef;
242                 }
243         }
244
245         delete $passwd->{name}{${$eref}[0]};
246         delete $passwd->{uid}{${$eref}[2]};
247 }
248
249 sub group_remove_entry($$)
250 {
251         my ($group, $eref) = @_;
252
253         for (my $i = 0; defined($group->{array}[$i]); $i++) {
254                 if ($eref == $group->{array}[$i]) {
255                         $group->{array}[$i] = undef;
256                 }
257         }
258
259         delete $group->{name}{${$eref}[0]};
260         delete $group->{gid}{${$eref}[2]};
261 }
262
263 sub passwd_save($)
264 {
265         my ($passwd) = @_;
266         my @lines = ();
267         my $path = $passwd->{path};
268         my $tmppath = $path.$$;
269
270         foreach my $eref (@{$passwd->{array}}) {
271                 next unless defined($eref);
272
273                 my $line = join(':', @{$eref});
274                 push(@lines, $line);
275         }
276
277         open(PWD, ">$tmppath") or die("Unable to open '$tmppath' for write");
278         print PWD join("\n", @lines)."\n";
279         close(PWD);
280         rename($tmppath, $path) or die("Unable to rename $tmppath => $path");
281 }
282
283 sub group_save($)
284 {
285         my ($group) = @_;
286         my @lines = ();
287         my $path = $group->{path};
288         my $tmppath = $path.$$;
289
290         foreach my $eref (@{$group->{array}}) {
291                 next unless defined($eref);
292
293                 my $line = join(':', @{$eref});
294                 if (scalar(@{$eref}) == 3) {
295                         $line .= ":";
296                 }
297                 push(@lines, $line);
298         }
299
300         open(GROUP, ">$tmppath") or die("Unable to open '$tmppath' for write");
301         print GROUP join("\n", @lines)."\n";
302         close(GROUP);
303         rename($tmppath, $path) or die("Unable to rename $tmppath => $path");
304 }
305
306 sub passwd_add($$)
307 {
308         my ($path, $name) = @_;
309
310         #print "passwd_add: '$name' in '$path'\n";
311
312         my $passwd = passwd_load($path);
313
314         my $e = passwd_lookup_name($passwd, $name);
315         die("account[$name] already exists in '$path'") if defined($e);
316
317         my $uid = passwd_get_free_uid($passwd);
318         my $gid = 65534;# nogroup gid
319
320         my $pwent = $name.":x:".$uid.":".$gid.":".$name." gecos:/nodir:/bin/false";
321
322         passwd_add_entry($passwd, $pwent);
323
324         passwd_save($passwd);
325
326         return 0;
327 }
328
329 sub passwd_delete($$)
330 {
331         my ($path, $name) = @_;
332
333         #print "passwd_delete: '$name' in '$path'\n";
334
335         my $passwd = passwd_load($path);
336
337         my $e = passwd_lookup_name($passwd, $name);
338         die("account[$name] does not exists in '$path'") unless defined($e);
339
340         passwd_remove_entry($passwd, $e);
341
342         passwd_save($passwd);
343
344         return 0;
345 }
346
347 sub group_add($$)
348 {
349         my ($path, $name) = @_;
350
351         #print "group_add: '$name' in '$path'\n";
352
353         my $group = group_load($path);
354
355         my $e = group_lookup_name($group, $name);
356         die("group[$name] already exists in '$path'") if defined($e);
357
358         my $gid = group_get_free_gid($group);
359
360         my $gwent = $name.":x:".$gid.":".""; #no members yet
361
362         group_add_entry($group, $gwent);
363
364         group_save($group);
365
366         #printf("%d\n", $gid);
367
368         return 0;
369 }
370
371 sub group_delete($$)
372 {
373         my ($path, $name) = @_;
374
375         #print "group_delete: '$name' in '$path'\n";
376
377         my $group = group_load($path);
378
379         my $e = group_lookup_name($group, $name);
380         die("group[$name] does not exists in '$path'") unless defined($e);
381
382         group_remove_entry($group, $e);
383
384         group_save($group);
385
386         return 0;
387 }