#!/usr/bin/perl # migrate old samba2 smb.conf settings to new samba3 setup # as well as merge local configuration settings # Dec 3 2003 Stew Benedict # revised Jan 6 2004 - dropping some parameters # revised Feb 11 2004 - don't try to process a config a second time # check command line arguments my $numargs = @ARGV; if ($numargs lt 1) { print "useage: smb-migrate test|test-commit|commit\n"; exit(1); } # define some variables my $user_parms = 0; my $new_conf_file = "/etc/samba/smb.conf"; my @new_conf; my $merged_conf_file = "/etc/samba/smb.conf"; my $merge_comment = "# *** merged from original smb.conf: ***\n"; my $uncomment_comment = "# *** uncommented from original smb.conf: ***\n"; my $unique_comment = "# *** unique added from original smb.conf: ***\n"; my @merge_log; my $log_file = "/var/log/samba/smb-migrate.log"; my $to_merge = "/etc/samba/smb.conf.tomerge"; if ($ARGV[0] eq "test" || $ARGV[0] eq "test-commit") { $to_merge = "smb.conf"; $log_file = "smb-migrate.log"; $merged_conf_file = "smb.conf.merged"; } # if the file has already been processed, don't do it again my $processed = `grep -c 'original smb.conf: ***' $to_merge`; if ($processed > 0) { `cp $to_merge $new_conf_file`; print "Already processed, aborting.\n"; exit 0; } # get the stripped, uncommented data from old smb.conf my @old_conf = `grep -v "^#" $to_merge | grep -v "^;" | grep -v "^\$"` or die; # use a clean config file as a starting point `cp /usr/share/samba/smb.conf.clean $new_conf_file` if $ARGV[0] !~ /test/; # and the whole new conf file we're going to merge with my @new_conf_org = `cat $new_conf_file` or die; mlog("Data to change/add in standard sections of smb.conf:\n\n"); sub mlog { my (@dstring) = @_; if ($ARGV[0] eq "test") { print "@dstring"; } else { push @merge_log, @dstring; } } sub merge_conf { my ($header, $new_value) = @_; my @parmlist = split " = ", $new_value; my $match = 0; my $comment = ''; $comment = $unique_comment if $continuation = 0; $continuation = 1; # find the header in question $index = 0; foreach (@new_conf_org) { if (/^\[$header\]|;\[$header\]|^; \[$header\]|^\[$header\$\]|;\[$header\$\]|^; \[$header\$\]/) { # restore print$ $header = "print" . '$' if $header eq "print"; # if the header is commented, remove the comment if (/^;\[|^#\[|^; \[/) { my $entry = $_; @new_conf_org[$index] =~ s/^;|^; |^#//g; mlog("uncomment: $header line $index: $entry -> @new_conf_org[$index]"); splice(@new_conf_org, $index, 0, $uncomment_comment); $index++ } $start_loc = $index; # print "[$header]: $start_loc\n"; last; } $index++ } my $elements = @new_conf_org; # walk through this header's entries, update as needed for ($i = $start_loc + 1; $i < $elements; $i++) { # if we hit a new header, may be commented - bail out my $is_header = @new_conf_org[$i]; $is_header =~ s/^ |\t|\n//; if ($is_header =~ /^\[|;\[|#\[/) { # print "new header: $is_header at $i\n"; if ($match == 0) { # it's possible the parameter is continued across multiple lines $continuation = 0 if $new_value !~ /\\$/; # completely new entry, try to place it under the correct header # print "new entry for [$header]: $new_value\n"; mlog("unique: $header line $last_index: $new_value"); splice(@new_conf_org, $last_index + 1, 0, $comment, $new_value); $last_index++;$last_index++; } return; } # some syntax changes if ($new_value =~ /winbind/) { $old_value = $new_value; $new_value =~ s/winbind/idmap/; mlog("syntax: $header: $old_value -> $new_value"); } # partial match, decide whether to add or replace if (@new_conf_org[$i] =~ /@parmlist[0]/) { if (@new_conf_org[$i] !~ /^;|^#/) { if (@new_conf_org[$i] ne $_) { mlog("update: $header line $i: @new_conf_org[$i] -> $new_value"); @new_conf_org[$i] = ";" . $new_conf_org[$i]; splice(@new_conf_org, $i + 1, 0, $merge_comment, $new_value); # $match = 1; } $match = 1; } else { # is it really a definition or just a comment? if (@new_conf_org[$i] =~ / = /) { # commented in new config, add the old entry mlog("add: $header line $i: @new_conf_org[$i] -> $new_value"); splice(@new_conf_org, $i + 1, 0, $merge_comment, $new_value); $i++;$i++; $match = 1; } } # $match = 1 if $new_value eq @new_conf_org[$i]; $last_index = $i; return if ($match eq 1); $match = 0; } } return; } foreach (@old_conf) { # check for section headers if (/^\[/) { # standard headers? if (!/^\[global\]|^\[homes\]|^\[netlogon\]|^\[Profiles\]|^\[printers\]|^\[print\$\]|^\[pdf-generator\]/) { # non-standard - add to new config $user_parms = 1; push (@new_conf, $_); } else { $user_parms = 0; chop; $header = $_; s/\[|\]|\$//g; $bare_header = $_; } } else { # non-standard - add to new config if ($user_parms == 1) { push (@new_conf, $_); } else { # now we're working with standard settings # update new config with values if they differ or are commented out # translate any old nomenclature to the new style # may still be some commented lines buried # throw those out and try to merge into new config if (!/^[ ]+#|^[ ]+;|^#|^;/) { # print "$header: $_\n"; merge_conf($bare_header, $_); } } } } # write the user config data to new smb.conf mlog("\nNew data for smb.conf:\n\n"); mlog("@new_conf"); if ($ARGV[0] eq "commit" || $ARGV[0] eq "test-commit") { local *NEWCONF; open(NEWCONF, "> $merged_conf_file"); print NEWCONF @new_conf_org; print NEWCONF @new_conf; close NEWCONF; local *LOGFILE; open(LOGFILE, "> $log_file"); print LOGFILE @merge_log; close LOGFILE }