88fdc7bca607c33c74e03a54ab28086d4d97846f
[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 passwd_save($)
250 {
251         my ($passwd) = @_;
252         my @lines = ();
253         my $path = $passwd->{path};
254         my $tmppath = $path.$$;
255
256         foreach my $eref (@{$passwd->{array}}) {
257                 next unless defined($eref);
258
259                 my $line = join(':', @{$eref});
260                 push(@lines, $line);
261         }
262
263         open(PWD, ">$tmppath") or die("Unable to open '$tmppath' for write");
264         print PWD join("\n", @lines)."\n";
265         close(PWD);
266         rename($tmppath, $path) or die("Unable to rename $tmppath => $path");
267 }
268
269 sub group_save($)
270 {
271         my ($group) = @_;
272         my @lines = ();
273         my $path = $group->{path};
274         my $tmppath = $path.$$;
275
276         foreach my $eref (@{$group->{array}}) {
277                 next unless defined($eref);
278
279                 my $line = join(':', @{$eref});
280                 if (scalar(@{$eref}) == 3) {
281                         $line .= ":";
282                 }
283                 push(@lines, $line);
284         }
285
286         open(GROUP, ">$tmppath") or die("Unable to open '$tmppath' for write");
287         print GROUP join("\n", @lines)."\n";
288         close(GROUP);
289         rename($tmppath, $path) or die("Unable to rename $tmppath => $path");
290 }
291
292 sub passwd_add($$)
293 {
294         my ($path, $name) = @_;
295
296         #print "passwd_add: '$name' in '$path'\n";
297
298         my $passwd = passwd_load($path);
299
300         my $e = passwd_lookup_name($passwd, $name);
301         die("account[$name] already exists in '$path'") if defined($e);
302
303         my $uid = passwd_get_free_uid($passwd);
304         my $gid = 65534;# nogroup gid
305
306         my $pwent = $name.":x:".$uid.":".$gid.":".$name." gecos:/nodir:/bin/false";
307
308         passwd_add_entry($passwd, $pwent);
309
310         passwd_save($passwd);
311
312         return 0;
313 }
314
315 sub passwd_delete($$)
316 {
317         my ($path, $name) = @_;
318
319         #print "passwd_delete: '$name' in '$path'\n";
320
321         my $passwd = passwd_load($path);
322
323         my $e = passwd_lookup_name($passwd, $name);
324         die("account[$name] does not exists in '$path'") unless defined($e);
325
326         passwd_remove_entry($passwd, $e);
327
328         passwd_save($passwd);
329
330         return 0;
331 }
332
333 sub group_add($$)
334 {
335         my ($path, $name) = @_;
336
337         #print "group_add: '$name' in '$path'\n";
338
339         my $group = group_load($path);
340
341         my $e = group_lookup_name($group, $name);
342         die("group[$name] already exists in '$path'") if defined($e);
343
344         my $gid = group_get_free_gid($group);
345
346         my $gwent = $name.":x:".$gid.":".""; #no members yet
347
348         group_add_entry($group, $gwent);
349
350         group_save($group);
351
352         #printf("%d\n", $gid);
353
354         return 0;
355 }
356
357 sub group_delete($$)
358 {
359         my ($path, $name) = @_;
360
361         #print "group_delete: '$name' in '$path'\n";
362
363         die("group_delete: not implemented yet!");
364
365         return 0;
366 }