File Coverage

lib/Sysync.pm
Criterion Covered Total %
statement 247 306 80.7
branch 71 148 47.9
condition 18 36 50.0
subroutine 19 32 59.3
pod 27 27 100.0
total 382 549 69.5


line stmt bran cond sub pod time code
1             package Sysync;
2 1     1   314344 use strict;
  1         2  
  1         47  
3 1     1   5 use Digest::MD5 qw(md5_hex);
  1         2  
  1         83  
4 1     1   7 use File::Find;
  1         2  
  1         62  
5 1     1   6 use File::Path;
  1         2  
  1         5481  
6              
7             our $VERSION = '0.38';
8              
9             =head1 NAME
10              
11             Sysync - Simplistic system management
12              
13             =head1 SYNOPSIS
14              
15             See: http://sysync.nongnu.org/tutorial.html
16              
17             =head1 METHODS
18              
19             =head3 new
20              
21             Creates a new Sysync object.
22              
23             my $sysync = Sysync->new({
24             sysdir => '/var/sysync',
25             stagedir => '/var/sysync/stage', # if omitted, appends ./stage to sysdir
26             salt_prefix => '', # if omitted, defaults to '$6$'
27             log => $file_handle_for_logging,
28             });
29              
30             =cut
31              
32             sub new
33             {
34 1     1 1 1430 my ($class, $params) = @_;
35             my $self = {
36             sysdir => $params->{sysdir},
37             stagedir => ($params->{stagedir} || "$params->{sysdir}/stage"),
38             stagefilesdir => ($params->{stagefiledir} || "$params->{sysdir}/stage-files"),
39             salt_prefix => (exists($params->{salt_prefix}) ? $params->{salt_prefix} : '$6$'),
40             extra_users => $params->{extra_users},
41             log => $params->{log},
42 1 50 33     25 };
      33        
43              
44 1         4 bless($self, $class);
45              
46 1         3 return $self;
47             }
48              
49             =head3 log
50              
51             Log a message.
52              
53             $self->log('the moon is broken');
54              
55             =cut
56              
57             sub log
58             {
59 17     17 1 30 my $self = shift;
60 17         423 my $lt = localtime;
61 17         84 my $log = $self->{log};
62              
63 17         334 print $log "$lt: $_[0]\n";
64             }
65              
66             =head3 sysdir
67              
68             Returns the base system directory for sysync.
69              
70             =cut
71              
72 123     123 1 274 sub sysdir { shift->{sysdir} }
73              
74             =head3 stagedir
75              
76             Returns stage directory.
77              
78             =cut
79              
80 2 50   2 1 9 sub stagedir { $_[0]->{stagedir} || join('/', $_[0]->sysdir, 'stage' ) }
81              
82             =head3 stagefilesdir
83              
84             Returns stage-files directory.
85              
86             =cut
87              
88 1 50   1 1 5 sub stagefilesdir { $_[0]->{stagefilesdir} || join('/', $_[0]->sysdir, 'stage-files' ) }
89              
90             =head3 get_user
91              
92             Returns hashref of user information. It's worth noting that passwords should not be returned here for normal users.
93              
94             Example:
95              
96             {
97             username => 'wafflewizard',
98             uid => 1001,
99             fullname => 'Waffle E. Wizzard',
100             homedir => '/home/wafflewizard',
101             shell => '/bin/bash',
102             disabled => 0,
103             ssh_keys => [
104             'ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA10YAFEAByOlrMmd5Beh73SOg7okHpK5Bz9dOgmYb4idR3A6iz+ycyXtnCmwSGdmh6AQoeKfJx+9rxLtvdHUzhRa/YejqBGsTwYl5Q+1bKbCkJfgZhtB99Xt5j7grXzrJ0zp2vTfG2mPndnD7xuQQQnLsZrFSoTY8FPvQo3a9R1wPIuxBGs5jWm9+pvluJtAT3I7IaVfylNBCGU8+Fw/qvJtWEesyqyRmFJZ47XzFKJ5EzB6hLaW+MAaCH6fZDycdjiTfJOMThtpFF557rqz5EN76VRqHpnkiqKpatMX4h0hiL/Snl+fbUxOYm5qcHughuis4Sf6xXoABsyz2lsrqiQ== wafflewizard',
105             ],
106             }
107              
108             =cut
109              
110 0     0 1 0 sub get_user { die 'needs implemented' }
111              
112             =head3 get_all_users
113              
114             Return array of all usernames.
115              
116             =cut
117              
118 0     0 1 0 sub get_all_users { die 'needs implemented' }
119              
120             =head3 get_user_password
121              
122             Return a user's encrypted password.
123              
124             =cut
125              
126 0     0 1 0 sub get_user_password { die 'needs implemented' }
127              
128             =head3 set_user_password
129              
130             Set a user's encrypted password.
131              
132             =cut
133              
134 0     0 1 0 sub set_user_password { die 'needs implemented' }
135              
136             =head3 get_users_from_group
137              
138             Returns array of users in a given group.
139              
140             =cut
141              
142 0     0 1 0 sub get_users_from_group { die 'needs implemented' }
143              
144             =head3 get_all_groups
145              
146             Returns array of all groups.
147              
148             =cut
149              
150 0     0 1 0 sub get_all_groups { die 'needs implemented' }
151              
152             =head3 get_all_hosts
153              
154             Returns all hosts.
155              
156             =cut
157              
158 0     0 1 0 sub get_all_hosts { die 'needs implemented' }
159              
160             =head3 must_refresh
161              
162             Returns true if sysync must refresh.
163              
164             Passing 1 or 0 as an argument sets whether this returns true.
165              
166             =cut
167              
168 0     0 1 0 sub must_refresh { die 'needs implemented' }
169              
170             =head3 must_refresh_files
171              
172             Returns true if sysync must refresh managed files.
173              
174             Passing 1 or 0 as an argument sets whether this returns true.
175              
176             =cut
177              
178 0     0 1 0 sub must_refresh_files { die 'needs implemented' }
179              
180             =head3 generate_user_line
181              
182             Generate a line for both the user and shadow file.
183              
184             =cut
185              
186             sub generate_user_line
187             {
188 84     84 1 162 my ($self, $user, $what) = @_;
189              
190 84   100     287 my $gid = $user->{gid} || $user->{uid};
191 84   66     262 my $fullname = $user->{fullname} || $user->{username};
192              
193 84         113 my $password = '*';
194              
195 84 100       159 if ($user->{password})
196             {
197 6         12 $password = $user->{password};
198             }
199             else
200             {
201 78         220 my $p = $self->get_user_password($user->{username});
202              
203 78 50       204 $password = $p if $p;
204             }
205              
206 84         126 my $line = q[];
207 84 100       178 if ($what eq 'passwd')
    50          
208             {
209             $line = join(':', $user->{username}, 'x', $user->{uid}, $gid,
210 42         201 $fullname, $user->{homedir}, $user->{shell});
211             }
212             elsif ($what eq 'shadow')
213             {
214 42 50       85 my $password = $user->{disabled} ? '!' : $password;
215 42         162 $line = join(':', $user->{username}, $password, 15198, 0, 99999, 7, '','','');
216             }
217              
218 84         244 return $line;
219             }
220              
221             =head3 generate_group_line
222              
223             Generate a line for the group file.
224              
225             =cut
226              
227             sub generate_group_line
228             {
229 216     216 1 357 my ($self, $group, $what) = @_;
230              
231 216   100     262 my $users = join(',', @{$group->{users} || []}) || '';
232              
233 216         326 my $line = '';
234 216 100       411 if ($what eq 'group')
    50          
235             {
236 108         347 $line = join(':', $group->{groupname}, 'x', $group->{gid}, $users);
237             }
238             elsif ($what eq 'gshadow')
239             {
240 108         363 $line = sprintf('%s:*::%s', $group->{groupname}, $users);
241             }
242             }
243              
244             =head3 is_valid_host
245              
246             Returns true if host is valid.
247              
248             =cut
249              
250 0     0 1 0 sub is_valid_host { die 'needs implemented' }
251              
252             =head3 get_host_user
253              
254             Given a host, then a username, return a hashref with user details.
255              
256             =cut
257              
258             sub get_host_user
259             {
260 0     0 1 0 my ($self, $host, $username) = @_;
261              
262 0         0 my $data = $self->get_host_users_groups($host);
263 0 0       0 my @users = @{$data->{users} || []};
  0         0  
264              
265 0         0 return (grep { $username eq $_->{username} } @users)[0];
  0         0  
266             }
267              
268             =head3 get_host_group
269              
270             Given a host, then a group name, return a hashref with group details.
271              
272             =cut
273              
274             sub get_host_group
275             {
276 0     0 1 0 my ($self, $host, $groupname) = @_;
277              
278 0         0 my $data = $self->get_host_users_groups($host);
279 0 0       0 my @groups = @{$data->{groups} || []};
  0         0  
280              
281 0         0 return (grep { $groupname eq $_->{groupname} } @groups)[0];
  0         0  
282             }
283              
284             =head3 get_host_users
285              
286             Given a host return a hashref with user details.
287              
288             =cut
289              
290             sub get_host_users
291             {
292 2     2 1 4 my ($self, $host, $username) = @_;
293              
294 2         9 my $data = $self->get_host_users_groups($host);
295 2 50       5 my @users = @{$data->{users} || []};
  2         9  
296              
297 2         2 my %u = map { $_->{username} => $_ } @users;
  28         40  
298              
299 2         26 return \%u;
300             }
301              
302             =head3 get_host_groups
303              
304             Given a host return a hashref with group details.
305              
306             =cut
307              
308             sub get_host_groups
309             {
310 2     2 1 5 my ($self, $host, $groupname) = @_;
311              
312 2         5 my $data = $self->get_host_users_groups($host);
313 2 50       4 my @groups = @{$data->{groups} || []};
  2         15  
314              
315 2         5 my %g = map { $_->{groupname} => $_ } @groups;
  72         127  
316              
317 2         38 return \%g;
318             }
319              
320             =head3 get_host_ent
321              
322             For a generate all of the password data, including ssh keys, for a specific host.
323              
324             =cut
325              
326             sub get_host_ent
327             {
328 3     3 1 681 my ($self, $host) = @_;
329              
330 3 50       40 return unless $self->is_valid_host($host);
331              
332 3         13 my $data = $self->get_host_users_groups($host);
333 3 50       7 my @users = @{$data->{users} || []};
  3         22  
334 3 50       6 my @groups = @{$data->{groups} || []};
  3         23  
335              
336 3         9 my $passwd = join("\n", map { $self->generate_user_line($_, 'passwd') } @users) . "\n";
  42         125  
337 3         13 my $shadow = join("\n", map { $self->generate_user_line($_, 'shadow') } @users) . "\n";
  42         95  
338 3         15 my $group = join("\n", map { $self->generate_group_line($_, 'group',) } @groups) . "\n";
  108         214  
339 3         18 my $gshadow = join("\n", map { $self->generate_group_line($_, 'gshadow',) } @groups) . "\n";
  108         204  
340              
341 3         17 my @ssh_keys;
342 3         9 for my $user (@users)
343             {
344 42 100       88 next unless $user->{ssh_keys};
345              
346 3 0 33     10 if ($user->{disabled} and $user->{ssh_keys})
347             {
348 0         0 $_ = "# $_" for @{$user->{ssh_keys}};
  0         0  
349 0         0 unshift @{$user->{ssh_keys}}, '### ACCOUNT DISABLED VIA SYSYNC';
  0         0  
350             }
351              
352 3 50       6 my $keys = join("\n", @{$user->{ssh_keys} || []});
  3         33  
353 3 50       13 $keys .= "\n" if $keys;
354              
355 3 50       9 next unless $keys;
356              
357             push @ssh_keys, {
358             username => $user->{username},
359             keys => $keys,
360             uid => $user->{uid},
361 3         60 };
362             }
363              
364             return {
365 3         101 passwd => $passwd,
366             shadow => $shadow,
367             group => $group,
368             gshadow => $gshadow,
369             ssh_keys => \@ssh_keys,
370             data => $data,
371             };
372             }
373              
374             =head3 get_host_files
375              
376             Generate a list of files with their content.
377              
378             Returns hashref:
379             '/etc/filename.conf' => {
380             mode => 600,
381             gid => 0,
382             uid => 0,
383             data => 'data is here'
384             }
385              
386             =cut
387              
388 0     0 1 0 sub get_host_files { die 'needs implemented' }
389              
390             =head3 update_host_files
391              
392             Build host files from specifications.
393              
394             =cut
395              
396             sub update_host_files
397             {
398 1     1 1 718 my ($self, $host) = @_;
399              
400 1         6 my $stagefilesdir = $self->stagefilesdir;
401 1         4 my $stagedir = $self->stagedir;
402              
403 1         2 my $r = 0;
404              
405 1 50       8 next unless $self->is_valid_host($host);
406 1         6 my $files = $self->get_host_files($host);
407              
408 1 50       122 unless (-d "$stagefilesdir/$host")
409             {
410 1         154 mkdir "$stagefilesdir/$host";
411 1         60 chmod 0755, "$stagefilesdir/$host";
412 1         19 chown 0, 0, "$stagefilesdir/$host";
413              
414 1         9 $self->log("creating: $stagefilesdir/$host");
415 1         4 $r++;
416             }
417              
418 1 50       68 unless (-d "$stagefilesdir/$host/etc")
419             {
420 1         179 mkdir "$stagefilesdir/$host/etc";
421 1         39 chmod 0755, "$stagefilesdir/$host/etc";
422 1         17 chown 0, 0, "$stagefilesdir/$host/etc";
423              
424 1         10 $self->log("creating: $stagefilesdir/$host/etc");
425 1         3 $r++;
426             }
427              
428 1 50       53 unless (-d "$stagefilesdir/$host/etc/ssh")
429             {
430 1         132 mkdir "$stagefilesdir/$host/etc/ssh";
431 1         61 chmod 0755, "$stagefilesdir/$host/etc/ssh";
432 1         23 chown 0, 0, "$stagefilesdir/$host/etc/ssh";
433              
434 1         10 $self->log("creating: $stagefilesdir/$host/etc/ssh");
435 1         3 $r++;
436             }
437              
438 1 50       53 unless (-d "$stagefilesdir/$host/etc/ssh/authorized_keys")
439             {
440 1         116 mkdir "$stagefilesdir/$host/etc/ssh/authorized_keys";
441 1         35 chmod 0755, "$stagefilesdir/$host/etc/ssh/authorized_keys";
442 1         16 chown 0, 0, "$stagefilesdir/$host/etc/ssh/authorized_keys";
443              
444 1         8 $self->log("creating: $stagefilesdir/$host/etc/ssh/authorized_keys");
445 1         3 $r++;
446             }
447              
448 1 50       3 for my $path (sort keys %{ $files || {} })
  1         8  
449             {
450 1         3 my $item = $files->{$path};
451 1 50       6 next unless $item->{directory};
452              
453 0         0 $item->{directory} =~ s/\/$//;
454              
455 0 0       0 next if $item->{directory} eq '/etc';
456 0 0       0 next if $item->{directory} eq '/etc/ssh';
457 0 0       0 next if $item->{directory} eq '/etc/ssh/authorized_keys';
458              
459 0         0 $item->{directory} =~ s/^\///;
460              
461 0         0 my @path_parts = split('/', $item->{directory});
462 0         0 my $filename = pop @path_parts;
463 0         0 my $parent_dir = join('/', @path_parts);
464              
465 0         0 $item->{file} =~ s/^\///;
466              
467 0 0       0 unless (-d "$stagefilesdir/$host/$parent_dir")
468             {
469 0         0 die "[$host: error] parent directory $parent_dir not defined for $item->{directory}\n";
470             }
471              
472 0 0       0 unless (-d "$stagefilesdir/$host/$item->{directory}")
473             {
474 0         0 mkdir "$stagefilesdir/$host/$item->{directory}";
475 0         0 $self->log("creating: $stagefilesdir/$host/$item->{directory}");
476             }
477              
478 0         0 my $mode = sprintf("%04i", $item->{mode});
479 0         0 chmod $mode, "$stagefilesdir/$host/$item->{directory}";
480 0         0 chown $item->{uid}, $item->{gid}, "$stagefilesdir/$host/$item->{directory}";
481              
482 0         0 $r++;
483             }
484              
485 1 50       2 for my $path (keys %{ $files || {} })
  1         5  
486             {
487 1         3 my $item = $files->{$path};
488 1 50       4 next unless $item->{file};
489              
490 1         5 my @path_parts = split('/', $item->{file});
491              
492 1         2 my $filename = pop @path_parts;
493 1         4 my $parent_dir = join('/', @path_parts);
494              
495 1         5 $item->{file} =~ s/^\///;
496              
497 1 50       61 unless (-d "$stagefilesdir/$host/$parent_dir")
498             {
499 0         0 die "[$host: error] directory $parent_dir not defined for $item->{file}\n";
500             }
501              
502 1 50       11 if ($self->write_file_contents("$stagefilesdir/$host/$item->{file}", $item->{data}))
503             {
504 1         3 $r++;
505             }
506              
507 1         9 my $mode = sprintf("%04i", $item->{mode});
508              
509 1         35 chmod oct($mode), "$stagefilesdir/$host/$item->{file}";
510 1         25 chown $item->{uid}, $item->{gid}, "$stagefilesdir/$host/$item->{file}";
511             }
512              
513             # get list of staging directory contents
514 1         3 my @staged_file_list;
515             File::Find::find({
516 5     5   530 wanted => sub { push @staged_file_list, $_ },
517 1         174 no_chdir => 1,
518             }, "$stagefilesdir/$host");
519              
520 1         9 for my $staged_file (@staged_file_list)
521             {
522 5 50       116 next unless -e $staged_file;
523              
524 5         54 (my $local_staged_file = $staged_file) =~ s/^$stagefilesdir\/$host//;
525 5 100       13 next unless $local_staged_file;
526              
527 4 50       11 next if $local_staged_file eq '/';
528 4 100       11 next if $local_staged_file eq '/etc';
529 3 100       8 next if $local_staged_file eq '/etc/ssh';
530 2 100       6 next if $local_staged_file eq '/etc/ssh/authorized_keys';
531 1 50       4 next if $local_staged_file eq '/var/lib/extrausers';
532              
533 1 50       5 unless ($files->{$local_staged_file})
534             {
535 0 0       0 if (-d $staged_file)
    0          
536             {
537 0         0 $self->log("deleting directory: $staged_file");
538 0         0 rmtree($staged_file);
539             }
540             elsif (-e $staged_file)
541             {
542 0         0 $self->log("deleting file: $staged_file");
543 0         0 unlink($staged_file);
544             }
545 0         0 $r++;
546             }
547             }
548              
549 1         9 return $r;
550             }
551              
552             =head3 update_all_hosts
553              
554             Iterate through every host and build password files.
555              
556             =cut
557              
558             sub update_all_hosts
559             {
560 1     1 1 678 my ($self, %params) = @_;
561              
562             # get list of hosts along with image name
563 1   33     5 my $hosts = $params{hosts} || $self->get_all_hosts;
564              
565             # first, build staging directories
566 1 50       3 my @hosts = keys %{ $hosts->{hosts} || {} };
  1         6  
567              
568 1         8 my $stagedir = $self->stagedir;
569              
570 1         3 my $r = 0;
571              
572 1 50 33     8 if ($hosts->{extra_users} || $self->{extra_users})
573             {
574 0         0 $hosts->{passwd_file} = '/var/lib/extrausers/passwd';
575 0         0 $hosts->{group_file} = '/var/lib/extrausers/group';
576 0         0 $hosts->{shadow_file} = '/var/lib/extrausers/shadow';
577 0         0 $hosts->{gshadow_file} = '/var/lib/extrausers/gshadow';
578             }
579              
580 1   50     5 my $passwd_file = $hosts->{passwd_file} || '/etc/passwd';
581 1   50     8 my $group_file = $hosts->{group_file} || '/etc/group';
582 1   50     23 my $shadow_file = $hosts->{shadow_file} || '/etc/shadow';
583 1   50     5 my $gshadow_file = $hosts->{gshadow_file} || '/etc/gshadow';
584              
585 1         3 for my $host (@hosts)
586             {
587 1 50       7 next unless $self->is_valid_host($host);
588              
589 1 50       72 unless (-d "$stagedir/$host")
590             {
591 1         198 mkdir "$stagedir/$host";
592 1         37 chmod 0755, "$stagedir/$host";
593 1         18 chown 0, 0, "$stagedir/$host";
594 1         15 $self->log("creating: $stagedir/$host");
595 1         3 $r++;
596             }
597              
598 1 50       54 unless (-d "$stagedir/$host/etc")
599             {
600 1         108 mkdir "$stagedir/$host/etc";
601 1         34 chmod 0755, "$stagedir/$host/etc";
602 1         17 chown 0, 0, "$stagedir/$host/etc";
603 1         8 $self->log("creating: $stagedir/$host/etc");
604 1         2 $r++;
605             }
606              
607 1 50       49 unless (-d "$stagedir/$host/etc/ssh")
608             {
609 1         108 mkdir "$stagedir/$host/etc/ssh";
610 1         45 chmod 0755, "$stagedir/$host/etc/ssh";
611 1         20 chown 0, 0, "$stagedir/$host/etc/ssh";
612 1         10 $self->log("creating: $stagedir/$host/etc/ssh");
613 1         3 $r++;
614             }
615              
616 1 50       35 unless (-d "$stagedir/$host/etc/ssh/authorized_keys")
617             {
618 1         97 mkdir "$stagedir/$host/etc/ssh/authorized_keys";
619 1         46 chmod 0755, "$stagedir/$host/etc/ssh/authorized_keys";
620 1         49 chown 0, 0, "$stagedir/$host/etc/ssh/authorized_keys";
621 1         9 $self->log("creating: $stagedir/$host/etc/ssh/authorized_keys");
622 1         3 $r++;
623             }
624              
625 1 50       52 unless (-d "$stagedir/$host/var")
626             {
627 1         126 mkdir "$stagedir/$host/var";
628 1         33 chmod 0755, "$stagedir/$host/var";
629 1         16 chown 0, 0, "$stagedir/$host/var";
630              
631 1         8 $self->log("creating: $stagedir/$host/var");
632 1         4 $r++;
633             }
634              
635 1 50       75 unless (-d "$stagedir/$host/var/lib")
636             {
637 1         85 mkdir "$stagedir/$host/var/lib";
638 1         30 chmod 0755, "$stagedir/$host/var/lib";
639 1         18 chown 0, 0, "$stagedir/$host/var/lib";
640              
641 1         8 $self->log("creating: $stagedir/$host/var/lib");
642 1         4 $r++;
643             }
644              
645 1 50       47 unless (-d "$stagedir/$host/var/lib/extrausers")
646             {
647 1         150 mkdir "$stagedir/$host/var/lib/extrausers";
648 1         33 chmod 0755, "$stagedir/$host/var/lib/extrausers";
649 1         17 chown 0, 0, "$stagedir/$host/var/lib/extrausers";
650              
651 1         9 $self->log("creating: $stagedir/$host/var/lib/extrausers");
652 1         4 $r++;
653             }
654              
655             # write host files
656 1         6 my $ent_data = $self->get_host_ent($host);
657              
658 1 50       4 next unless $ent_data;
659              
660 1 50       3 for my $key (@{ $ent_data->{ssh_keys} || [] })
  1         5  
661             {
662 1         4 my $username = $key->{username};
663 1         4 my $uid = $key->{uid};
664 1         3 my $text = $key->{keys};
665              
666 1 50       15 if ($self->write_file_contents("$stagedir/$host/etc/ssh/authorized_keys/$username", $text))
667             {
668 1         39 chmod 0600, "$stagedir/$host/etc/ssh/authorized_keys/$username";
669 1         25 chown $uid, 0, "$stagedir/$host/etc/ssh/authorized_keys/$username";
670 1         5 $r++;
671             }
672             }
673              
674             my ($shadow_group) =
675 36         79 grep { $_->{groupname} eq 'shadow' }
676 1 50       3 @{ $ent_data->{data}{groups} || [ ] };
  1         6  
677              
678 1 50       4 $shadow_group = {} unless defined $shadow_group;
679 1   50     5 $shadow_group = $shadow_group->{gid} || 0;
680              
681 1 50       6 if ($self->write_file_contents("$stagedir/$host/$passwd_file", $ent_data->{passwd}))
682             {
683 1         31 chmod 0644, "$stagedir/$host/$passwd_file";
684 1         21 chown 0, 0, "$stagedir/$host/$passwd_file";
685 1         3 $r++;
686             }
687              
688 1 50       7 if ($self->write_file_contents("$stagedir/$host/${group_file}", $ent_data->{group}))
689             {
690 1         30 chmod 0644, "$stagedir/$host/${group_file}";
691 1         18 chown 0, 0, "$stagedir/$host/${group_file}";
692 1         3 $r++;
693             }
694              
695 1 50       7 if ($self->write_file_contents("$stagedir/$host/${shadow_file}", $ent_data->{shadow}))
696             {
697 1         61 chmod 0640, "$stagedir/$host/${shadow_file}";
698 1         26 chown 0, $shadow_group, "$stagedir/$host/${shadow_file}";
699 1         3 $r++;
700             }
701              
702 1 50       8 if ($self->write_file_contents("$stagedir/$host/${gshadow_file}", $ent_data->{gshadow}))
703             {
704 1         31 chmod 0640, "$stagedir/$host/${gshadow_file}";
705 1         42 chown 0, $shadow_group, "$stagedir/$host/${gshadow_file}";
706 1         44 $r++;
707             }
708             }
709              
710 1         7 return $r;
711             }
712              
713             =head3 write_file_contents
714              
715             =cut
716              
717             sub write_file_contents
718             {
719 6     6 1 18 my ($self, $file, $data) = @_;
720              
721             # check to see if this differs
722              
723 6 50       281 if (-e $file)
724             {
725 0 0       0 if (md5_hex($data) eq md5_hex($self->read_file_contents($file)))
726             {
727 0         0 return;
728             }
729             }
730              
731 6         48 $self->log("writing: $file");
732              
733 6 50       105 if (-e $file)
734             {
735 0 0       0 unlink($file) or die $!;
736             }
737              
738 6 50       860 open(F, "> $file") or die $!;
739 6         60 print F $data;
740 6         294 close(F);
741              
742 6         41 return 1;
743             }
744              
745             =head3 read_file_contents
746              
747             =cut
748              
749             sub read_file_contents
750             {
751 100     100 1 187 my ($self, $file, %params) = @_;
752              
753             die "error: $file does not exist\n"
754 100 50 33     264 if $params{must_exist} and not -f $file;
755              
756 100 100       1465 return unless -e $file;
757              
758 22         856 open(my $fh, $file);
759 22         825 my @content = <$fh>;
760 22         211 close($fh);
761              
762 22         287 return join('', @content);
763             }
764              
765             1;
766              
767              
768             =head1 COPYRIGHT
769              
770             L L
771              
772             =head1 LICENSE
773              
774             Copyright (C) 2012, 2013 Bizowie
775              
776             This file is part of Sysync.
777              
778             Sysync is free software: you can redistribute it and/or modify
779             it under the terms of the GNU Affero General Public License as
780             published by the Free Software Foundation, either version 3 of the
781             License, or (at your option) any later version.
782              
783             Sysync is distributed in the hope that it will be useful,
784             but WITHOUT ANY WARRANTY; without even the implied warranty of
785             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
786             GNU Affero General Public License for more details.
787              
788             You should have received a copy of the GNU Affero General Public License
789             along with this program. If not, see .
790              
791             =head1 AUTHOR
792              
793             Michael J. Flickinger, C<< >>
794              
795             =cut