components/net-snmp-57/sun/masfcnv
changeset 5867 445e2cf1c845
parent 252 ee0fb1eabcbf
equal deleted inserted replaced
5866:683c5c035a79 5867:445e2cf1c845
       
     1 #
       
     2 # Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
       
     3 #
       
     4 # ident  "@(#)migration.pl	1.5 03/06/26 SMI"
       
     5 #
       
     6 # $Id: masfcnv,v 1.3 2004/01/09 14:04:14 rr144420 Exp $
       
     7 
       
     8 =head1 NAME
       
     9 
       
    10 masfcnv - SNMP configuration migration script
       
    11 
       
    12 =head1 SYNOPSIS
       
    13 
       
    14 masfcnv S<[ -cimnrs ]> S<[ -l agent|master ]> S<[ -p enable|disable|error ]>
       
    15 S<[ -t none|add ]> S<[ -u agent|master|error ]> S<[ -y agent|master|error ]>
       
    16 
       
    17 masfcnv [ -V ]
       
    18 
       
    19 masfcnv [ -? ]
       
    20 
       
    21 =head1 DESCRIPTION
       
    22 
       
    23 The masfcnv script is used to assist the system administrator in migrating an
       
    24 existing set of configuration files for the Sun SNMP Management Agent for Sun
       
    25 Fire and Netra Systems (MASF) to the Systems Management Agent (SMA).
       
    26 
       
    27 The script accepts as input the currently installed set of MASF and SMA
       
    28 configuration files and outputs a new set of SMA configuration files. Existing
       
    29 SMA configuration files are backed up by appending ".bak" to the filename. The
       
    30 administrator may choose to output the new configuration to the standard output
       
    31 instead of replacing the current configuration by specifying the -n option.
       
    32 
       
    33 The migration script must be run as the superuser and failure to do so will
       
    34 cause the script to exit with an error message. Before running the script the
       
    35 administrator should ensure that both the SMA and MASF agents are not running.
       
    36 If the agents are running they will be shut down by the script.
       
    37 
       
    38 The migration script will install a new startup script for the MASF agent in
       
    39 /etc/init.d and backup the old script. During migration, MASF will be
       
    40 configured as an AgentX subagent of SMA. All migration settings will be
       
    41 migrated to the SMA configuration file.
       
    42 
       
    43 The migration script will abort if any unrecognised directives are found in
       
    44 either the MASF configuration files or the SMA configuration files. This may
       
    45 be overridden with the -i option. If this option is selected, the behaviour is
       
    46 to retain unrecognised directives which were present in the SMA configuration,
       
    47 but remove those present in the MASF configuration.
       
    48 
       
    49 The migration script will then proceed to migrate access control and trap
       
    50 configuration. As a side-effect of running the migration script, the following
       
    51 directives may be expanded by the script into multiple directives with
       
    52 an equivalent interpretation:
       
    53 
       
    54 =over
       
    55 
       
    56 =item rwcommunity
       
    57 
       
    58 =item rocommunity
       
    59 
       
    60 =item rwuser
       
    61 
       
    62 =item rouser
       
    63 
       
    64 =item trapcommunity
       
    65 
       
    66 =item trapsink
       
    67 
       
    68 =item trap2sink
       
    69 
       
    70 =item informsink
       
    71 
       
    72 =back
       
    73 
       
    74 =head2 Access Control Migration
       
    75 
       
    76 Access control directives will be expanded into the equivalent com2sec, group,
       
    77 access and view directives. Existing group names will be renamed by prepending
       
    78 a prefix to avoid conflict with any which may already be defined in SMA.
       
    79 
       
    80 When migrating SNMPv1 or v2c access control, a conflict may occur if both MASF
       
    81 and SMA configuration files have defined access permissions for the same
       
    82 community and source address. The default behaviour is to abort with a
       
    83 message, unless the -y option specifies otherwise. If "-y agent" was specified
       
    84 then the MASF configuration will take precedence. If "-y master" was specified
       
    85 then the SMA configuration will be retained.
       
    86 
       
    87 When migrating USM configuration (SNMPv3), a conflict may occur if both SMA
       
    88 and MASF configurations define a user with the same securityName. If this
       
    89 occurs then the behaviour of the script is determined by the -u option.
       
    90 If "-u agent" has been selected then the configuration of the user defined in
       
    91 the MASF configuration files will be the one that is retained. Otherwise, if
       
    92 the "-u master" option has been selected, the one defined in the SMA
       
    93 configuration files will be retained.
       
    94 
       
    95 The migration script will by default attempt to migrate USM users from MASF to
       
    96 SMA. The script will determine whether there are any existing SNMPv3 users
       
    97 present in the SMA configuration and whether or not the default engineID has
       
    98 been overridden in the SMA configuration files. If neither of these are found to
       
    99 be the case then the any usmUser statements containing localised
       
   100 authentication keys can be migrated to SMA, along with the MASF engineID. This
       
   101 will result in the engineID of SMA master agent changing.
       
   102 
       
   103 If the script determines that there are existing SNMPv3 users or a manually
       
   104 configured engineID present in the SMA configuration, then only those users
       
   105 defined in createUser statements will be transferred. Those users which were
       
   106 defined in usmUser statements will be transferred but will have their
       
   107 passwords reset to a random value. The administrator is advised to notify
       
   108 their users of their new password and/or reset the password themselves by
       
   109 editing the newly-generated configuration file themselves.
       
   110 
       
   111 =head2 Trap/Inform Migration
       
   112 
       
   113 The migration script will perform a check to determine whether a trap
       
   114 destination defined for MASF is already specified in an existing SMA trapsink,
       
   115 trap2sink or informsink directive. If this is the case then the directive in
       
   116 the MASF configuration will be discarded to avoid duplicate traps/informs being
       
   117 received.
       
   118 
       
   119 trapsink, trap2sink and informsink directives specified in the existing SMA
       
   120 configuration are considered valid destinations for MASF traps/informs and will
       
   121 receive them from the MASF subagent after migration.
       
   122 
       
   123 If the "-t none" option was specified on the command line, then the migration
       
   124 script will carry over any remaining MASF trap/inform directives without
       
   125 modification.
       
   126 
       
   127 If the "-t add" option was specified (the default), then the migration script
       
   128 will expand any trapsink, trap2sink or informsink directives to use the
       
   129 TARGET-MIB and NOTIFICATION-MIB. The TARGET-MIB specifies targets using IP
       
   130 addresses, so it may be desirable to use the "-t none" option if, for example,
       
   131 the network allocates IP addresses to hostnames dynamically via DHCP.
       
   132 
       
   133 The expanded directives will define filters specific to the MASF agent so that
       
   134 traps from other subagents will not be received by migrated trap destinations.
       
   135 Existing filters present in the SMA configuration will by default not be
       
   136 modified and may or may not receive MASF traps depending upon the filters
       
   137 which were originally defined for them.
       
   138 
       
   139 If the -l option is specified, then any filters already defined in the
       
   140 TARGET-MIB and the NOTIFICATION-MIB for SMA will be extended to include traps
       
   141 from MASF. In the event that a trap destination is already configured in the
       
   142 TARGET-MIB with the same target address and community as an existing MASF
       
   143 trap/inform sink, a conflict will arise. 
       
   144 
       
   145 If "-l agent" was specified and a conflict arises, then the migration script
       
   146 will use the target SNMP parameters (i.e. SNMP version and choise of
       
   147 trap/inform) defined by the MASF trap/informsink directive to send traps to
       
   148 this destination. Otherwise if the "-l master" option was specified, then the
       
   149 conflict will be resolved using the target SNMP parameters specified in the
       
   150 SMA configuration.
       
   151 
       
   152 =head2 Miscellaneous
       
   153 
       
   154 If the migration script encounters any of the following directives in the MASF
       
   155 configuration file and they are either not present or differ in the SMA
       
   156 configuration, the script will log a warning message:
       
   157 
       
   158 =over
       
   159 
       
   160 =item syslocation
       
   161 
       
   162 =item syscontact
       
   163 
       
   164 =item sysname
       
   165 
       
   166 =item sysservices
       
   167 
       
   168 =item agentgroup
       
   169 
       
   170 =item agentuser
       
   171 
       
   172 =item authtrapenable
       
   173 
       
   174 =back
       
   175 
       
   176 =head1 OPTIONS
       
   177 
       
   178 =over
       
   179 
       
   180 =item B<-?>
       
   181 
       
   182 =item B<--help>
       
   183 
       
   184 Displays usage information.
       
   185 
       
   186 =item B<-c>
       
   187 
       
   188 =item B<--no-community>
       
   189 
       
   190 Do not transfer v1/v2c communities
       
   191 
       
   192 =item B<-i>
       
   193 
       
   194 =item B<--ignore-unrecognized-directives>
       
   195 
       
   196 Continue processing if unrecognised directives are present.
       
   197 
       
   198 =item B<-l> I<agent|master>
       
   199 
       
   200 =item B<--master-trap-target>=I<agent|master>
       
   201 
       
   202 If 'agent' is specified then the existing SMA trap targets will be configured
       
   203 to receive traps that were previously sent to destinations for the Sun Fire
       
   204 SNMP agent. If 'master' is specified then the targets will be configured to
       
   205 receive Sun Fire SNMP traps but existing SNMP target parameters will be used.
       
   206 
       
   207 =item B<-m>
       
   208 
       
   209 =item B<--no-usmuser>
       
   210 
       
   211 Do not transfer usm (v3) users
       
   212 
       
   213 =item B<-n>
       
   214 
       
   215 =item B<--dry-run>
       
   216 
       
   217 Run the migration without modifying any files. If any error arises then
       
   218 continue processing. This can be used to determine the likely migration issues.
       
   219 
       
   220 =item B<-p> I<enable|disable|error>
       
   221 
       
   222 =item B<--use-agent-port>=I<enable|disable|error>
       
   223 
       
   224 Indicates whether the port originally used by the Sun Fire SNMP agent should be
       
   225 used by the SMA agent after migration (if the two agents are using different
       
   226 ports). If 'enable' is specified then the port used by the Sun Fire SNMP agent
       
   227 will also be used by the SMA agent after migration. If 'disable' is specified
       
   228 then the ports used by SMA will not be updated by the migration tool.  If
       
   229 the 'error' option is specified and the SMA agent is not already using the same
       
   230 ports as those used by the original Sun Fire SNMP agent then an error will be
       
   231 reported and the migration process will be terminated. If no option is
       
   232 specified the default behaviour is equivalent to the 'error' flag.
       
   233 
       
   234 =item B<-r>
       
   235 
       
   236 =item B<--no-trap>
       
   237 
       
   238 Do not transfer trap destinations
       
   239 
       
   240 =item B<-s>
       
   241 
       
   242 =item B<--skip-user>
       
   243 
       
   244 If a user is found in the MASF configuration file that cannot be created in the
       
   245 new configuration due to a change in the engine ID, then output a message
       
   246 indicating that the user could not be migrated (needs to be manually recreated)
       
   247 and continue processing. If this option is not present then the migration tool
       
   248 will consider such a situation as an error and abort.
       
   249 
       
   250 =item B<-t> I<none|add>
       
   251 
       
   252 =item B<--trap-filter>=I<none|add>
       
   253 
       
   254 If 'none' is specified then the script will copy trap directives directly. The
       
   255 administrator may need to manually update the configuration file to ensure
       
   256 traps are only delivered to their intended destinations.  If 'add' is specifed
       
   257 then trap filters will be constructed so that traps originating from the
       
   258 original Sun Fire SNMP agent are only delivered to the destinations that
       
   259 originally received them. 'add' is the default behaviour.
       
   260 
       
   261 =item B<-u> I<agent|master|error>
       
   262 
       
   263 =item B<--select-user>=I<agent|master|error>
       
   264 
       
   265 Specifies that if a user with the same name is found in both configuration
       
   266 files that the conflict is to be resolved using the specified configuration
       
   267 file as input.  Selecting a user from a particular will also cause the group
       
   268 declaration for that user to be taken from the same file. If 'agent' is
       
   269 specified then the user will be taken from the configuration file for the Sun
       
   270 Fire SNMP Agent. If 'master' is specified then the user will be taken from the
       
   271 SMA configuration.  Otherwise if 'error' is given, the script will terminate.
       
   272 If this option is not present then the default behaviour is equivalent to
       
   273 the 'error' flag.
       
   274 
       
   275 =item B<-V>
       
   276 
       
   277 =item B<--version>
       
   278 
       
   279 Display the version of this script.
       
   280 
       
   281 =item B<-y> I<agent|master|error>
       
   282 
       
   283 =item B<--select-community>=I<agent|master|error>
       
   284 
       
   285 Specifies that if a community and source (hostname, IP addr or range of IP
       
   286 addresses) is found to be conflicting in the two configurations, which
       
   287 combination should be selected when mapping to a security name.  If 'agent' is
       
   288 specified then the community and source information will be taken from the
       
   289 configuration for the Sun Fire SNMP agent.  If 'master' is specified it will be
       
   290 taken from the SMA agent.  Otherwise if 'error' is specified an error will be
       
   291 reported and the migration will terminate.  If this option is not present then
       
   292 the default behaviour is equivalent to the 'error' flag.
       
   293 
       
   294 =back
       
   295 
       
   296 =head1 EXIT STATUS
       
   297 
       
   298 The script will exit with 0 if migration was successful, non-zero if a problem
       
   299 occurred during migration.
       
   300 
       
   301 =head1 EXAMPLES
       
   302 
       
   303 For a simple migration which will fail if there are any potential conflicts:
       
   304 
       
   305 masfcnv
       
   306 
       
   307 To migrate the MASF configuration such that it will always succeed, MASF
       
   308 settings will override in the event of a conflict with SMA and access will
       
   309 still be provided on the original MASF port:
       
   310 
       
   311 masfcnv -is -l agent -p enable -u agent -y agent
       
   312 
       
   313 To attempt a dry run and migrate the configuration such that any conflicts
       
   314 will be resolved by retaining existing SMA settings:
       
   315 
       
   316 masfcnv -l master -u master -y master
       
   317 
       
   318 =head1 FILES
       
   319 
       
   320 =over
       
   321 
       
   322 =item /etc/sma/snmp/snmpd.conf
       
   323 
       
   324 =item /var/sma_snmp/snmpd.conf
       
   325 
       
   326 SMA configuration files
       
   327 
       
   328 =back
       
   329 
       
   330 =over
       
   331 
       
   332 =item /etc/opt/SUNWmasf/conf/snmpd.conf
       
   333 
       
   334 =item /var/opt/SUNWmasf/snmpd.dat
       
   335 
       
   336 MASF configuration files
       
   337 
       
   338 =back
       
   339 
       
   340 =over
       
   341 
       
   342 =item /tmp/sma_migration.log
       
   343 
       
   344 masfcnv log file
       
   345 
       
   346 =back
       
   347 
       
   348 =cut
       
   349 
       
   350 use strict;
       
   351 
       
   352 # Text::ParseWords requires 5.005
       
   353 require 5.005;
       
   354 
       
   355 use Getopt::Long 2.17;
       
   356 use Text::ParseWords;
       
   357 use Net::hostent;
       
   358 use Socket;
       
   359 use Data::Dumper;
       
   360 use File::Copy;
       
   361 use Text::Wrap;
       
   362 
       
   363 ##############################################################################
       
   364 # global defaults
       
   365 
       
   366 # storage for new lines
       
   367 %::ADDED_CONFIGS = ('prepend'=>[],
       
   368 	'append'=>[]);
       
   369 
       
   370 use vars qw ($INTERNET_OID
       
   371     $LOG_FILE
       
   372     $DATA_DIR
       
   373     $FILTER_TYPE_INCLUDED
       
   374     $FILTER_TYPE_EXCLUDED
       
   375     $ENTITY_MIB_OID
       
   376     $SUNPLAT_MIB_OID
       
   377     $SNMP_UDP_DOMAIN
       
   378     $DEFAULT_ROW_STATUS
       
   379     $DEFAULT_STORAGE_TYPE
       
   380     $MPMODEL_SNMPV1
       
   381     $MPMODEL_SNMPV2C
       
   382     $MPMODEL_SNMPV2U
       
   383     $MPMODEL_SNMPV3
       
   384     $SECURITY_MODEL_ANY
       
   385     $SECURITY_MODEL_SNMPV1
       
   386     $SECURITY_MODEL_SNMPV2C
       
   387     $SECURITY_MODEL_USM
       
   388     $SECURITY_LEVEL_NOAUTHNOPRIV
       
   389     $SECURITY_LEVEL_AUTHNOPRIV
       
   390     $SECURITY_LEVEL_AUTHPRIV
       
   391     $NOTIFY_TYPE_TRAP
       
   392     $NOTIFY_TYPE_INFORM);
       
   393 # location where template files, etc. are stored.
       
   394 *LOG_FILE = \"/tmp/sma_migration.log";
       
   395 *INTERNET_OID = \".1.3.6.1";
       
   396 *DATA_DIR = \"/usr/lib/net_snmp";
       
   397 *FILTER_TYPE_INCLUDED = \1;
       
   398 *FILTER_TYPE_EXCLUDED = \2;
       
   399 *ENTITY_MIB_OID = \".1.3.6.1.2.1.47";
       
   400 *SUNPLAT_MIB_OID = \".1.3.6.1.4.1.42.2.70.101";
       
   401 *SNMP_UDP_DOMAIN = \".1.3.6.1.6.1.1";
       
   402 *DEFAULT_ROW_STATUS = \1;
       
   403 *DEFAULT_STORAGE_TYPE = \3;
       
   404 *MPMODEL_SNMPV1 = \0;
       
   405 *MPMODEL_SNMPV2C = \1;
       
   406 *MPMODEL_SNMPV2U = \2;
       
   407 *MPMODEL_SNMPV3 = \3;
       
   408 *SECURITY_MODEL_ANY = \0;
       
   409 *SECURITY_MODEL_SNMPV1 = \1;
       
   410 *SECURITY_MODEL_SNMPV2C = \2;
       
   411 *SECURITY_MODEL_USM = \3;
       
   412 *SECURITY_LEVEL_NOAUTHNOPRIV = \1;
       
   413 *SECURITY_LEVEL_AUTHNOPRIV = \2;
       
   414 *SECURITY_LEVEL_AUTHPRIV = \3;
       
   415 *NOTIFY_TYPE_TRAP = \1;
       
   416 *NOTIFY_TYPE_INFORM = \2;
       
   417 
       
   418 
       
   419 ##############################################################################
       
   420 # misc functions
       
   421 
       
   422 sub log_message
       
   423 {
       
   424 	my ($msg, $line) = @_;
       
   425 	if (defined $line && exists $line->{'file'} && exists
       
   426 		$line->{'lineno'}) {
       
   427 		$msg = $line->{'file'}.', line '.($line->{'lineno'} + 1).
       
   428 		    ': '.$msg;
       
   429 	}
       
   430 	$msg = wrap('', '', $msg);
       
   431 	if ($::AUTOMATED) {
       
   432 	  open (LOG, ">> $LOG_FILE") || 
       
   433 	  	die "Couldn't open log file $LOG_FILE\n";
       
   434 	  print LOG $msg;
       
   435 	  print STDERR $msg;
       
   436 	  close LOG;
       
   437 	} else {
       
   438 		print STDERR $msg;
       
   439 	}
       
   440 }
       
   441 
       
   442 sub get_backup_filename
       
   443 {
       
   444     my ($fname) = @_;
       
   445     if ( -e $fname.'.bak') {
       
   446 	my ($i) = 0;
       
   447 	while ( -e $fname.".bak.$i" ) {
       
   448 	    $i++;
       
   449 	}
       
   450 	return $fname.".bak.$i";
       
   451     }
       
   452     return $fname.".bak";
       
   453 }
       
   454 
       
   455 sub backup_files
       
   456 {
       
   457     log_message "Backing up original files\n";
       
   458     my ($f, $newf);
       
   459     for $f (@::MASF_CONFIG_FILES, $::MASF_PERSISTENT_FILE, @::SMA_CONFIG_FILES,
       
   460 	    $::SMA_PERSISTENT_FILE) {
       
   461 	if (! -e $f) {
       
   462 	    next;
       
   463 	}
       
   464 	$newf = get_backup_filename($f);
       
   465 	log_message "Backing up $f to $newf\n";
       
   466 	if (0 == copy $f, $newf) {
       
   467 	    log_message "Couldn't backup $f - aborting\n";
       
   468 	    exit 1;
       
   469 	}
       
   470     }
       
   471 }
       
   472 
       
   473 sub remove_masf_persistent_file
       
   474 {
       
   475     if (! -e $::MASF_PERSISTENT_FILE) {
       
   476 	return;
       
   477     }
       
   478     log_message "Removing $::MASF_PERSISTENT_FILE\n";
       
   479     if (0 == unlink $::MASF_PERSISTENT_FILE) {
       
   480 	log_message "Couldn't remove MASF persistent storage file\n".
       
   481 	"$::MASF_PERSISTENT_FILE - Aborting\n";
       
   482 	exit 1;
       
   483     }
       
   484 }
       
   485 
       
   486 sub version
       
   487 {
       
   488     my ($rev) = '$Revision: 1.3 $';
       
   489     $rev=~s/^\$Revision: //;
       
   490     $rev=~s/ \$$//;
       
   491     print STDERR "$0 $rev\n".
       
   492 	"Copyright 2003 Sun Microsystems, Inc.\n".
       
   493 	"All rights reserved.\n".
       
   494 	"Use is subject to license terms.\n";
       
   495     exit 0;
       
   496 }
       
   497 
       
   498 sub stop_sma_running
       
   499 {
       
   500     `/etc/init.d/init.sma stop`;
       
   501 }
       
   502 
       
   503 sub stop_masf_running
       
   504 {
       
   505     `/etc/init.d/masfd stop`;
       
   506     if (($? >> 8) > 0) {
       
   507 	log_message "Couldn't stop the MASF agent\n";
       
   508 	exit 1;
       
   509     }
       
   510 }
       
   511 
       
   512 sub set_umask
       
   513 {
       
   514     # only root should be able to read the config file
       
   515     umask 0077;
       
   516 }
       
   517 
       
   518 sub install_new_wrapper_script
       
   519 {
       
   520     log_message "Installing new MASF startup script\n";
       
   521 
       
   522     my $pkgInstance='SUNWmasfr';
       
   523     my @commands=("/usr/sbin/installf $pkgInstance /etc/init.d/masfd f 744 root sys",
       
   524     "/usr/sbin/installf $pkgInstance /etc/rc0.d/K40masfd=/etc/init.d/masfd l",
       
   525     "/usr/sbin/installf $pkgInstance /etc/rc1.d/K40masfd=/etc/init.d/masfd l",
       
   526     "/usr/sbin/installf $pkgInstance /etc/rc2.d/K40masfd=/etc/init.d/masfd l",
       
   527     "/usr/sbin/installf $pkgInstance /etc/rc3.d/S90masfd=/etc/init.d/masfd l",
       
   528     "/usr/sbin/installf $pkgInstance /etc/rcS.d/K40masfd=/etc/init.d/masfd l",
       
   529     "/usr/sbin/install -f /etc/init.d -m 0744 -u root -g sys $DATA_DIR/masfd",
       
   530     "/usr/sbin/installf -f $pkgInstance",
       
   531     "/usr/sbin/removef $pkgInstance /etc/rc3.d/S80masfd",
       
   532     "/usr/bin/rm /etc/rc3.d/S80masfd",
       
   533     "/usr/sbin/removef -f $pkgInstance"
       
   534     );
       
   535     my $command;
       
   536     for $command (@commands) {
       
   537 	`$command`;
       
   538 	if ($? >> 8) {
       
   539 	    log_message "A problem occurred whilst installing the MASF startup ".
       
   540 		"script\n";
       
   541 	    exit 1;
       
   542 	}
       
   543     }
       
   544 }
       
   545 
       
   546 sub install_template_config_file
       
   547 {
       
   548     if (copy($DATA_DIR.'/snmpd.conf', $::MASF_CONFIG_FILES[0]) == 0) {
       
   549 	log_message "Couldn't copy template configuration file to ".
       
   550 	    $::MASF_CONFIG_FILES[0]."\n";
       
   551 	    exit 1;
       
   552     }
       
   553 }
       
   554 
       
   555 sub are_we_root
       
   556 {
       
   557 	if ($> != 0) {
       
   558 		log_message "You are not running this as root.\n";
       
   559 		exit 1;
       
   560 	}
       
   561 }
       
   562 
       
   563 sub sma_config_sanity_check
       
   564 {
       
   565 	my ($files, $i);
       
   566 	# check we can read the main config file
       
   567 	if ( ! -r $::SMA_CONFIG_FILES[0] ) {
       
   568 		log_message "Couldn't read SMA config file ".
       
   569 		$::SMA_CONFIG_FILES[0]."\n";
       
   570 		exit 1;
       
   571 	}
       
   572 	for ($i = 0; $i < @::SMA_CONFIG_FILES; $i++) {
       
   573 		if (! -r $::SMA_CONFIG_FILES[$i]) {
       
   574 			splice @::SMA_CONFIG_FILES, $i--, 1;
       
   575 		}
       
   576 	}
       
   577 	# check that if the persistent storage file isn't present, that there
       
   578 	# aren't any stale backups indicating it failed to update it properly
       
   579 	if ( ! -e $::SMA_PERSISTENT_FILE ) {
       
   580 		$files = `/bin/ls $::SMA_PERSISTENT_DIR/sma.*.dat 2>/dev/null`;
       
   581 		if ($files ne '') {
       
   582 			log_message "Stale SMA agent config files found. The ".
       
   583 			"SMA agent may not have been shut down cleanly.\n";
       
   584 			exit 1;
       
   585 		} else {
       
   586 			# Assume this is because the agent was never run
       
   587 			log_message "WARNING: no SMA persistent storage file found\n";
       
   588 		}
       
   589 	}
       
   590 }
       
   591 
       
   592 sub masf_config_sanity_check
       
   593 {
       
   594     my ($files, $i);
       
   595     # check we can read the main config file
       
   596     if ( ! -r $::MASF_CONFIG_FILES[0] ) {
       
   597 	log_message "Couldn't read MASF config file ".
       
   598 	    $::MASF_CONFIG_FILES[0]."\n";
       
   599 	exit 1;
       
   600     }
       
   601     for ($i = 0; $i < @::MASF_CONFIG_FILES; $i++) {
       
   602 	if (! -r $::MASF_CONFIG_FILES[$i]) {
       
   603 	    splice @::MASF_CONFIG_FILES, $i--, 1;
       
   604 	}
       
   605     }
       
   606     # check that if the persistent storage file isn't present, that there
       
   607     # aren't any stale backups indicating it failed to update it properly
       
   608     if ( ! -e $::MASF_PERSISTENT_FILE ) {
       
   609 	$files = `/bin/ls $::MASF_PERSISTENT_DIR/snmpd.*.dat 2>/dev/null`;
       
   610 	if ($files ne '') {
       
   611 	    log_message "Stale MASF agent config files found. The MASF agent ".
       
   612 	    "may not have been shut down cleanly.\n";
       
   613 	    exit 1;
       
   614 	} else {
       
   615 	    # Assume this is because the agent was never run
       
   616 	    log_message "WARNING: no MASF persistent storage file found\n";
       
   617 	}
       
   618     }
       
   619 }
       
   620 
       
   621 sub hostnameToUDPDomain
       
   622 {
       
   623         my ($target) = @_;
       
   624         my ($hostname, $port, $hostent);
       
   625         my @dotted_decimal;
       
   626         ($hostname, $port) = ($target=~/^([^:]+):(\d+)$/);
       
   627         if ($port eq "") {
       
   628                 $port = 162;
       
   629         } 
       
   630         ($hostent) = gethostbyname($hostname);
       
   631         @dotted_decimal = unpack ('C4', $hostent->addr_list->[0]);
       
   632         return sprintf "0x%02x%02x%02x%02x%04x", @dotted_decimal, $port;
       
   633 
       
   634 }
       
   635 
       
   636 sub help
       
   637 {
       
   638 	my ($name) = ($0=~/([^\/]*)$/);
       
   639         print STDERR "Usage:
       
   640 $name	[ -cimnrs ] [ -l agent|master ] [ -p enable|disable|error ] 
       
   641 	[ -t none|add  ] [ -u agent|master|error ] [ -y agent|master|error ]
       
   642 	
       
   643 $name	[ -V ]
       
   644 
       
   645 $name	[ -? ]
       
   646 
       
   647         Migrates the configuration of the SNMP Agent for 
       
   648         Sun Fire Servers to the SMA SNMP Agent
       
   649 
       
   650 Option  Interpretation
       
   651 
       
   652 -? 
       
   653 --help
       
   654         Display this help message.
       
   655 
       
   656 -c
       
   657 --no-community
       
   658         Do not transfer v1/v2c communities
       
   659 
       
   660 -i
       
   661 --ignore-unrecognized-directives
       
   662         Continue processing if unrecognised directives are present.
       
   663 
       
   664 -l agent|master
       
   665 --master-trap-target=agent|master
       
   666         If 'agent' is specified then the existing SMA trap targets will
       
   667         be configured to receive traps that were previously sent to
       
   668         destinations for the Sun Fire SNMP agent. If 'master' is
       
   669         specified then the targets will be configured to receive Sun
       
   670         Fire SNMP traps but existing SNMP target parameters will be used. This
       
   671 	option may not be used with the \"-t none\" option.
       
   672 
       
   673 -m
       
   674 --no-usmuser
       
   675         Do not transfer usm (v3) users
       
   676 
       
   677 -n
       
   678 --dry-run
       
   679         Run the migration without modifying any files. If any error 
       
   680         arises then continue processing. This can be used to determine the
       
   681         likely migration issues.
       
   682 
       
   683 -p enable|disable|error
       
   684 --use-agent-port=enable|disable|error
       
   685         Indicates whether the port originally used by the Sun Fire SNMP
       
   686         agent should be used by the SMA agent after migration (if the
       
   687         two agents are using different ports). If 'enable' is specified
       
   688         then the port used by the Sun Fire SNMP agent will also be used
       
   689         by the SMA agent after migration. If 'disable' is specified then
       
   690         the ports used by SMA will not be updated by the migration tool.
       
   691         If the 'error' option is specified and the SMA agent is not 
       
   692         already using the same ports as those used by the original Sun
       
   693         Fire SNMP agent then an error will be reported and the migration
       
   694         process will be terminated. If no option is specified the default
       
   695         behaviour is equivalent to the 'error' flag.
       
   696 
       
   697 -r
       
   698 --no-trap
       
   699         Do not transfer trap destinations
       
   700 
       
   701 -s
       
   702 --skip-user
       
   703         If a user is found in the MASF configuration file that 
       
   704         cannot be created in the new configuration due to a change
       
   705         in the engine ID, then output a message indicating that
       
   706         the user could not be migrated (needs to be manually
       
   707         recreated) and continue processing. If this option is not
       
   708         present then the migration tool will consider such a 
       
   709         situation as an error and abort.
       
   710 
       
   711 -t none|add
       
   712 --trap-filter=none|add
       
   713         If 'none' is specified then the script will copy trap directives
       
   714         directly. The administrator may need to manually update the 
       
   715         configuration file to ensure traps are only delivered to their
       
   716         intended destinations.  If 'add' is specifed then trap filters
       
   717         will be constructed so that traps originating from the original
       
   718         Sun Fire SNMP agent are only delivered to the destinations that
       
   719         originally received them. 'add' is the default behaviour.
       
   720 
       
   721 -u agent|master|error
       
   722 --select-user=agent|master|error
       
   723         Specifies that if a user with the same name is found in both
       
   724         configuration files that the conflict is to be resolved using
       
   725         the specified configuration file as input.  Selecting a user
       
   726         from a particular will also cause the group declaration for 
       
   727         that user to be taken from the same file. If 'agent' is 
       
   728         specified then the user will be taken from the configuration
       
   729         file for the Sun Fire SNMP Agent. If 'master' is specified then
       
   730         the user will be taken from the SMA configuration.  Otherwise
       
   731         if 'error' is given, the script will terminate.  If this option
       
   732         is not present then the default behaviour is equivalent to the
       
   733         'error' flag.
       
   734 
       
   735 -V 
       
   736 --version
       
   737         Display the version of this script.
       
   738 
       
   739 -y agent|master|error
       
   740 --select-community=agent|master|error
       
   741         Specifies that if a community and source (hostname, IP addr
       
   742         or range of IP addresses) is found to be conflicting in the
       
   743         two configurations, which combination should be selected when
       
   744         mapping to a security name.  If 'agent' is specified then the
       
   745         community and source information will be taken from the 
       
   746         configuration for the Sun Fire SNMP agent.  If 'master' is
       
   747         specified it will be taken from the SMA agent.  Otherwise if 
       
   748         'error' is specified an error will be reported and the 
       
   749         migration will terminate.  If this option is not present then
       
   750         the default behaviour is equivalent to the 'error' flag.
       
   751 
       
   752 ";
       
   753 	exit 1;
       
   754 }
       
   755 
       
   756 %::SMA_CONFIGS = ();
       
   757 %::MASF_CONFIGS = ();
       
   758 sub strip_cr_nl
       
   759 {
       
   760     # remove \r and \n
       
   761     my ($lines, $i);
       
   762     for $lines (values %::SMA_CONFIGS) {
       
   763     	for ($i = 0; $i < @$lines; $i++) {
       
   764 		chomp $lines->[$i];
       
   765 	}
       
   766     }
       
   767     for $lines (values %::MASF_CONFIGS) {
       
   768     	for ($i = 0; $i < @$lines; $i++) {
       
   769 		chomp $lines->[$i];
       
   770 	}
       
   771     }
       
   772 }
       
   773 
       
   774 sub read_config_files
       
   775 {
       
   776     my ($i, @lines);
       
   777     for ($i = 0; $i < @::SMA_CONFIG_FILES; $i++) {
       
   778 	open (FH, '< '.$::SMA_CONFIG_FILES[$i]) || do {
       
   779 		log_message("Couldn't read config file ".
       
   780 		$::SMA_CONFIG_FILES[$i]."\n");
       
   781 		exit 1;
       
   782 	};
       
   783 	@lines=<FH>;
       
   784 	close FH;
       
   785 	$::SMA_CONFIGS{$::SMA_CONFIG_FILES[$i]} = [@lines];
       
   786     }
       
   787     if (-e $::SMA_PERSISTENT_FILE) {
       
   788 	open (FH, '< '.$::SMA_PERSISTENT_FILE) || do {
       
   789 	    log_message("Couldn't read config file ".$::SMA_PERSISTENT_FILE."\n");
       
   790 	    exit 1;
       
   791 	};
       
   792 	@lines=<FH>;
       
   793 	close FH;
       
   794 	$::SMA_CONFIGS{$::SMA_PERSISTENT_FILE} = [@lines];
       
   795     }
       
   796     for ($i = 0; $i < @::MASF_CONFIG_FILES; $i++) {
       
   797 	open (FH, '< '.$::MASF_CONFIG_FILES[$i]) || do {
       
   798 	    log_message("Couldn't read config file ".
       
   799 		    $::MASF_CONFIG_FILES[$i]."\n");
       
   800 	    exit 1;
       
   801 	};
       
   802 	@lines=<FH>;
       
   803 	close FH;
       
   804 	$::MASF_CONFIGS{$::MASF_CONFIG_FILES[$i]} = [@lines];
       
   805     }
       
   806     if (-e $::MASF_PERSISTENT_FILE) {
       
   807 	open (FH, '< '.$::MASF_PERSISTENT_FILE) || do {
       
   808 	    log_message("Couldn't read config file ".
       
   809 		    $::MASF_PERSISTENT_FILE."\n");
       
   810 	    exit 1;
       
   811 	};
       
   812 	@lines=<FH>;
       
   813 	close FH;
       
   814 	$::MASF_CONFIGS{$::MASF_PERSISTENT_FILE} = [@lines];
       
   815     }
       
   816     strip_cr_nl();
       
   817 }
       
   818 
       
   819 sub prompt_yes_no
       
   820 {
       
   821 	my ($prompt, $default) = @_;
       
   822 	my $response = '';
       
   823 	print STDOUT $prompt," [".$default."]:";
       
   824 	$response = <STDIN>;
       
   825 	chomp $response;
       
   826 	if ($response eq '') {
       
   827 		$response = $default;
       
   828 	}
       
   829 	while (uc($response)!~/^Y|N/) {
       
   830 	    print STDERR "Response must be (y)es or (n)o:";
       
   831 	    $response = <STDIN>;
       
   832 		chomp $response;
       
   833 	}
       
   834 	return $response=~/^[yY]/ ? 'yes' : 'no';
       
   835 }
       
   836 
       
   837 sub prompt
       
   838 {
       
   839 	my ($prompt, $default, $options) = @_;
       
   840 	my $response = '';
       
   841 	print STDOUT $prompt," [".$default."]:";
       
   842 	$response = <STDIN>;
       
   843 	chomp $response;
       
   844 	if ($response eq '') {
       
   845 		$response = $default;
       
   846 	}
       
   847 	while (scalar (grep $response eq $_, @$options) != 1) {
       
   848 	    print STDERR "Invalid response:";
       
   849 	    $response = <STDIN>;
       
   850 		chomp $response;
       
   851 	}
       
   852 	return $response;
       
   853 }
       
   854 
       
   855 sub parse_agentaddress
       
   856 {
       
   857     my ($line)=@_;
       
   858     my ($directive, $addresses)=($line=~/^\s*(agentaddress)\s+(.*\S)\s*$/);
       
   859     my ($addr, $spec, @addrs, $has_transport_specifier);
       
   860     @addrs=split /,/,$addresses;
       
   861     foreach $addr (@addrs) {
       
   862 	$has_transport_specifier=($addr=~/:/);
       
   863 	if (! $has_transport_specifier) {
       
   864 	    if ($addr=~/^\//) {
       
   865 		$spec = "unix";
       
   866 	    } else {
       
   867 		$spec = "udp";
       
   868 	    }
       
   869 	    $addr = $spec.':'.$addr;
       
   870 	}
       
   871     }
       
   872     return @addrs;
       
   873 }
       
   874 
       
   875 sub parse_config_line
       
   876 {
       
   877     my ($line)=@_;
       
   878     my @words;
       
   879     # strip whitespace
       
   880     $line=~s/^\s*//;
       
   881     $line=~s/\s*$//;
       
   882     # check to see if comment
       
   883     if ((substr $line, 0, 1) eq '#') {
       
   884 	return ('', ());
       
   885     }
       
   886     @words = &parse_line('\s+', 0, $line);
       
   887     if (! defined $words[0]) {
       
   888 	$words[0] = '';
       
   889     }
       
   890     return @words;
       
   891 }
       
   892 
       
   893 sub sanity_check_config_files 
       
   894 {
       
   895 	my @smaTokens = (
       
   896 		'master',
       
   897 		'agentxTimeout',
       
   898 		'agentxRetries',
       
   899 		'agentxPingInterval',
       
   900 		'targetParams',
       
   901 		'targetAddr',
       
   902 		'snmpNotifyFilterProfileTable',
       
   903 		'snmpNotifyTable',
       
   904 		'snmpNotifyFilterTable'
       
   905 			);
       
   906 	my @commonTokens = (
       
   907 		'rocommunity',
       
   908 		'rwcommunity',
       
   909 		'rouser',
       
   910 		'rwuser',
       
   911 		'agentaddress',
       
   912 		'trapsink',
       
   913 		'trap2sink',
       
   914 		'informsink',
       
   915 		'trapcommunity',
       
   916 		'com2sec',
       
   917 		'group',
       
   918 		'access',
       
   919 		'view',
       
   920 		'engineID',
       
   921 		'createUser',
       
   922 		'agentgroup',
       
   923 		'agentuser',
       
   924 		'authtrapenable',
       
   925 		'syslocation',
       
   926 		'syscontact',
       
   927 		'sysname',
       
   928 		'sysservices',
       
   929 		'engineBoots',
       
   930 		'oldEngineID',
       
   931 		'usmUser'
       
   932 		  );
       
   933 	my ($file, $i, $j, $directive, @tokens, $found, $warned, $answer);
       
   934 	# check sma config files
       
   935 	$warned = 0;
       
   936 	for $file (keys %::SMA_CONFIGS) {
       
   937 	    for ($i = 0; $i < @{$::SMA_CONFIGS{$file}}; $i++) {
       
   938 		$found = 0;
       
   939 		($directive, @tokens) = parse_config_line($::SMA_CONFIGS{$file}->[$i]);
       
   940 		if ($directive eq '') {
       
   941 			next;
       
   942 		}
       
   943 		
       
   944 		for ($j = 0; $j < @smaTokens; $j++) {
       
   945 		    if ($directive eq $smaTokens[$j]) {
       
   946 			$found = 1;
       
   947 			last;
       
   948 		    }
       
   949 		}
       
   950 		if ($found) {
       
   951 		    next;
       
   952 		}
       
   953 		for ($j = 0; $j < @commonTokens; $j++) {
       
   954 		    if ($directive eq $commonTokens[$j]) {
       
   955 			$found = 1;
       
   956 			last;
       
   957 		    }
       
   958 		}
       
   959 		if ($found) {
       
   960 		    next;
       
   961 		}
       
   962 		# token was not recognised
       
   963 		log_message("WARNING: Unrecognised token ".$directive.
       
   964 		" found in file ".$file." at line ".($i + 1)."\n");
       
   965 		$warned = 1;
       
   966 	    }
       
   967 	}	
       
   968 	for $file (keys %::MASF_CONFIGS) {
       
   969 	    for ($i = 0; $i < @{$::MASF_CONFIGS{$file}}; $i++) {
       
   970 		$found = 0;
       
   971 		($directive, @tokens) = parse_config_line($::MASF_CONFIGS{$file}->[$i]);
       
   972 		if ($directive eq '') {
       
   973 			next;
       
   974 		}
       
   975 
       
   976 		for ($j = 0; $j < @commonTokens; $j++) {
       
   977 		    if ($directive eq $commonTokens[$j]) {
       
   978 			$found = 1;
       
   979 			last;
       
   980 		    }
       
   981 		}
       
   982 		if ($found) {
       
   983 		    next;
       
   984 		}
       
   985 		# token was not recognised
       
   986 		log_message("WARNING: Unrecognised token ".$directive.
       
   987 		" found in file ".$file." at line ".($i + 1)."\n");
       
   988 		$warned = 1;
       
   989 		# remove the unrecognised token
       
   990 		splice @{$::MASF_CONFIGS{$file}}, $i--, 1;
       
   991 	    }
       
   992 	}	
       
   993 	if ($warned && ! $::IGNORE_UNRECOGNIZED_DIRECTIVES) {
       
   994 		if ($::AUTOMATED) {
       
   995 		    log_message("Unrecognised tokens found in configuration ".
       
   996 		    "file(s) - aborting\n");
       
   997 		    exit 1;
       
   998 		} else {
       
   999 			$answer = prompt_yes_no("Unrecognised tokens were found. Continue?", 'n');
       
  1000 			if ($answer eq 'no') {
       
  1001 				exit 1;
       
  1002 			}
       
  1003 		}
       
  1004 	}
       
  1005 }
       
  1006 
       
  1007 sub print_line
       
  1008 {
       
  1009     my ($fh, $line) = @_;
       
  1010     if (exists $line->{'orig'} && exists $line->{'new'}) {
       
  1011 	if ($line->{'orig'} ne $line->{'new'}) {
       
  1012 	    print $fh "# ### CHANGED BY $0 ###\n";
       
  1013 	    print $fh '# ',$line->{'orig'},"\n";
       
  1014 	    if (exists $line->{'comment'}) {
       
  1015 		print $fh $line->{'comment'},"\n";
       
  1016 	    }
       
  1017 	    print $fh $line->{'new'},"\n";
       
  1018 	} else {
       
  1019 	    print $fh $line->{'orig'},"\n";
       
  1020 	}
       
  1021     } elsif (exists $line->{'new'}) {
       
  1022 	if (exists $line->{'comment'}) {
       
  1023 	    print $fh $line->{'comment'},"\n";
       
  1024 	}
       
  1025 	print $fh $line->{'new'},"\n";
       
  1026     }
       
  1027     
       
  1028 }
       
  1029 
       
  1030 @::SMA_PERSISTENT_FILE_TOKENS = (
       
  1031 	'targetParams', 'targetAddr',
       
  1032 	'snmpNotifyFilterProfileTable', 'snmpNotifyTable',
       
  1033 	'snmpNotifyFilterTable', 'engineBoots', 'usmUser', 'oldEngineID',
       
  1034 	'createUser');
       
  1035 
       
  1036 # extract those directives destined for persistent storage
       
  1037 sub dump_persistent_storage
       
  1038 {
       
  1039     my ($file, $persistent_file, $config) = @_;
       
  1040     #list of tokens which should be in persistent storage
       
  1041     my ($f, $l, $lines);
       
  1042     for $f (keys %$config) {
       
  1043 	print $file "# ### IMPORTED FROM $f ###\n\n";
       
  1044 	for $l (@{$config->{$f}}) {
       
  1045 	    if (! exists $l->{'new'}) {
       
  1046 		next;
       
  1047 	    }
       
  1048 	    my ($directive, @toks) = parse_config_line($l->{'new'});
       
  1049 	    if ($directive) {
       
  1050 		my (@match) = grep ($_ eq $directive, @::SMA_PERSISTENT_FILE_TOKENS);
       
  1051 		if (@match == 0) {
       
  1052 		    # skip this line
       
  1053 		    next;
       
  1054 		}
       
  1055 	    } elsif ($f ne $persistent_file) {
       
  1056 		# skip all comments/blank lines which are not in the persistent
       
  1057 		# storage file
       
  1058 		next;
       
  1059 	    }
       
  1060 	    print_line($file, $l);
       
  1061 	}
       
  1062     }
       
  1063 }
       
  1064 
       
  1065 sub dump_config
       
  1066 {
       
  1067     my ($file, $persistent_file, $config) = @_;
       
  1068     #list of tokens which should be in persistent storage
       
  1069     my ($f, $l, $lines);
       
  1070     for $f (keys %$config) {
       
  1071 	print $file "# ### IMPORTED FROM $f ###\n\n";
       
  1072 	for $l (@{$config->{$f}}) {
       
  1073 	    if (! exists $l->{'new'}) {
       
  1074 		next;
       
  1075 	    }
       
  1076 	    my ($directive, @toks) = parse_config_line($l->{'new'});
       
  1077 	    if ($directive) {
       
  1078 		my (@match) = grep ($_ eq $directive, @::SMA_PERSISTENT_FILE_TOKENS);
       
  1079 		if (@match > 0) {
       
  1080 		    if ($f ne $persistent_file && exists $l->{'orig'}) {
       
  1081 			print $file "# moved to $::SMA_PERSISTENT_FILE >>> ".
       
  1082 			$l->{'orig'}."\n";
       
  1083 		    }
       
  1084 		    # skip this line
       
  1085 		    next;
       
  1086 		}
       
  1087 	    } elsif ($f eq $persistent_file) {
       
  1088 		# skip all comments/blank lines which are not in the persistent
       
  1089 		# storage file
       
  1090 		next;
       
  1091 	    }
       
  1092 	    print_line($file, $l);
       
  1093 	}
       
  1094     }
       
  1095 }
       
  1096 
       
  1097 sub set_union {
       
  1098 	my ($seta, $setb) = @_;
       
  1099 	my ($i, $j, $found, @out);
       
  1100 	for $i (@$seta, @$setb) {
       
  1101 		$found = 0;
       
  1102 		for $j (@out) {
       
  1103 			if ($j eq $i) {
       
  1104 				$found = 1;
       
  1105 			}
       
  1106 		}
       
  1107 		if (! $found) {
       
  1108 			push @out, $i;
       
  1109 		}
       
  1110 	}
       
  1111 	return @out;
       
  1112 }
       
  1113 
       
  1114 sub in_addr_to_number
       
  1115 {
       
  1116 	my ($in_addr) = @_;
       
  1117 	my ($i, $N) = (0, 0);
       
  1118 	my (@n) = (unpack 'C4', $in_addr);
       
  1119 	for ($i = 0; $i < @n; $i++) {
       
  1120 			$N = $N << 8;
       
  1121 			$N += $n[$i];
       
  1122 	}
       
  1123 	return $N;
       
  1124 }
       
  1125 
       
  1126 sub splice_line
       
  1127 {
       
  1128     my ($config, $line, $length, $offset, @lines) = @_;
       
  1129     my ($i, $f);
       
  1130     for $i (@lines) {
       
  1131 	# mark all the lines as changed
       
  1132 	$i->{'changed'} = undef;
       
  1133     }
       
  1134     for $f (values %$config) {
       
  1135 	for ($i = 0; $i < @$f; $i++) {
       
  1136 	    if ($f->[$i] eq $line) {
       
  1137 		if (@lines) {
       
  1138 		    splice @$f, $i + $offset, $length, @lines;
       
  1139 		} else {
       
  1140 		    splice @$f, $i + $offset, $length;
       
  1141 		}
       
  1142 		return;
       
  1143 	    }
       
  1144 	}
       
  1145     }
       
  1146 }
       
  1147 
       
  1148 sub prepend_line {
       
  1149     my ($line) = @_;
       
  1150     $line->{'changed'} = undef;
       
  1151     push @{$::ADDED_CONFIGS{'prepend'}}, $line;
       
  1152 }
       
  1153 
       
  1154 sub append_line {
       
  1155     my ($line) = @_;
       
  1156     $line->{'changed'} = undef;
       
  1157     push @{$::ADDED_CONFIGS{'append'}}, $line;
       
  1158 }
       
  1159 
       
  1160 sub replace_line {
       
  1161     my ($config, $line, @lines) = @_;
       
  1162     $line->{'meta'} = $lines[0]->{'meta'};
       
  1163     if (exists $lines[0]->{'new'}) {
       
  1164 	$line->{'new'} = $lines[0]->{'new'};
       
  1165     } else {
       
  1166 	delete $line->{'new'};
       
  1167     }
       
  1168     $lines[0] = $line;
       
  1169     splice_line($config, $line, 1, 0, @lines);
       
  1170 }
       
  1171 
       
  1172 sub insert_lines {
       
  1173     my ($config, $line, $after, @lines) = @_;
       
  1174     my ($offset) = ($after ? 1 : 0);
       
  1175     splice_line($config, $line, 0, $offset, @lines);
       
  1176 }
       
  1177 
       
  1178 sub get_lines
       
  1179 {
       
  1180     my ($directive, $config)=@_;
       
  1181     my ($line, $lines, @matches);
       
  1182     for $lines (values %$config) {
       
  1183 	for $line (@$lines) {
       
  1184 	    if (exists $line->{'meta'}->{'directive'} &&
       
  1185 		$line->{'meta'}->{'directive'} eq $directive) {
       
  1186 		    push @matches, $line;
       
  1187 	    }
       
  1188 	}
       
  1189     }
       
  1190     return @matches;
       
  1191 }
       
  1192 
       
  1193 ##############################################################################
       
  1194 # Traps
       
  1195 
       
  1196 sub remove_trap_destinations
       
  1197 {
       
  1198     my (@sinkLines) = (get_lines('trapsink', \%::MASF_CONFIGS),
       
  1199 	get_lines('trap2sink', \%::MASF_CONFIGS),
       
  1200 	get_lines('informsink', \%::MASF_CONFIGS));
       
  1201     my ($line);
       
  1202     for $line (@sinkLines) {
       
  1203 	replace_line(\%::MASF_CONFIGS, $line, {'meta'=>{}});
       
  1204     }
       
  1205 }
       
  1206 
       
  1207 sub get_filters
       
  1208 {
       
  1209     my ($keys, $configs)=@_;
       
  1210     my ($f, $l, @filters);
       
  1211     for $f (values %$configs) {
       
  1212 	for $l (@$f) {
       
  1213 	    if (! exists $l->{'meta'}->{'directive'} ||
       
  1214 		    ($l->{'meta'}->{'directive'} ne 'snmpNotifyFilterTable')) {
       
  1215 		next;
       
  1216 	    }
       
  1217 	    if (exists $keys->{'profileName'} && 
       
  1218 		    $l->{'meta'}->{'profileName'} ne $keys->{'profileName'})
       
  1219 	    {
       
  1220 		next;
       
  1221 	    }
       
  1222 	    push @filters, $l;
       
  1223 	}
       
  1224     }
       
  1225     return @filters;
       
  1226 }
       
  1227 
       
  1228 sub get_filterProfiles
       
  1229 {
       
  1230     my ($keys, $configs)=@_;
       
  1231     my ($f, $l, @targetAddrs);
       
  1232     for $f (values %$configs) {
       
  1233 	for $l (@$f) {
       
  1234 	    if (! exists $l->{'meta'}->{'directive'} ||
       
  1235 		    ($l->{'meta'}->{'directive'} ne 'snmpNotifyFilterProfileTable')) {
       
  1236 		next;
       
  1237 	    }
       
  1238 	    if (exists $keys->{'paramName'} && 
       
  1239 		    $l->{'meta'}->{'paramName'} ne $keys->{'paramName'}) {
       
  1240 		next;
       
  1241 	    }
       
  1242 	    if (exists $keys->{'profileName'} && 
       
  1243 		    $l->{'meta'}->{'profileName'} ne $keys->{'profileName'})
       
  1244 	    {
       
  1245 		next;
       
  1246 	    }
       
  1247 	    push @targetAddrs, $l;
       
  1248 	}
       
  1249     }
       
  1250     return @targetAddrs;
       
  1251 }
       
  1252 
       
  1253 sub tag_in_taglist
       
  1254 {
       
  1255     my ($tag, $taglist) = @_;
       
  1256     my (@tags) = split /[ \t\r\n]/,$taglist;
       
  1257     my ($t);
       
  1258     for $t (@tags) {
       
  1259 	if ($t eq $tag) {
       
  1260 	    return 1;
       
  1261 	}
       
  1262     }
       
  1263     return 0;
       
  1264 }
       
  1265 
       
  1266 sub notifyName_exists
       
  1267 {
       
  1268     my ($name) = @_;
       
  1269     my ($file, $line, $meta);
       
  1270     for $file (values %::MASF_CONFIGS, values %::SMA_CONFIGS) {
       
  1271 	for $line (@$file) {
       
  1272 	    $meta=$line->{'meta'};
       
  1273 	    if (exists $meta->{'notifyName'} &&
       
  1274 		    $meta->{'notifyName'} eq $name) {
       
  1275 		return 1;
       
  1276 	    }
       
  1277 	}
       
  1278     }
       
  1279     return 0;
       
  1280 }
       
  1281 
       
  1282 sub tag_exists
       
  1283 {
       
  1284     my ($tag) = @_;
       
  1285     my ($file, $line, $meta);
       
  1286     for $file (values %::MASF_CONFIGS, values %::SMA_CONFIGS) {
       
  1287 	for $line (@$file) {
       
  1288 	    $meta = $line->{'meta'};
       
  1289 	    if (exists $meta->{'tagList'} &&
       
  1290 			tag_in_taglist ($tag, $meta->{'tagList'})) {
       
  1291 		return 1;
       
  1292 	    }
       
  1293 	    if (exists $meta->{'notifyTag'} &&
       
  1294 		$tag eq $meta->{'notifyTag'}) {
       
  1295 		return 1;
       
  1296 	    }
       
  1297 	}
       
  1298     }
       
  1299     return 0;
       
  1300 }
       
  1301 
       
  1302 sub get_new_profileName
       
  1303 {
       
  1304     my ($i) = (0);
       
  1305     my ($try) = ('masfProfile'.$i);
       
  1306     while (profileName_exists($try)) {
       
  1307 	$i++;
       
  1308 	$try = 'masfProfile'.$i;
       
  1309     }
       
  1310     if (length $try > 32) {
       
  1311 	log_message "Unable to generate unique profileName\n";
       
  1312 	exit 1;
       
  1313     }
       
  1314     return $try;
       
  1315 }
       
  1316 
       
  1317 sub generate_notify_tags
       
  1318 {
       
  1319     my ($prefix) = @_;
       
  1320     my ($i) = (0);
       
  1321     while (tag_exists ($prefix.$i) ||
       
  1322 	notifyName_exists ($prefix.$i)) {
       
  1323 	$i++;
       
  1324     }
       
  1325     if (length ($prefix.$i) > 255) {
       
  1326 	log_message "Unable to generate valid tag for prefix ".$prefix."\n";
       
  1327 	exit 1;
       
  1328     }
       
  1329     return $prefix.$i;
       
  1330 }
       
  1331 
       
  1332 sub get_new_paramName
       
  1333 {
       
  1334     my ($i) = (0);
       
  1335     while (paramName_exists('masfParam'.$i)) {
       
  1336 	$i++;
       
  1337     }
       
  1338     if (length 'masfParam'.$i > 32) {
       
  1339 	log_message "Unable to generate unique paramName.\n";
       
  1340 	exit 1;
       
  1341     }
       
  1342     return 'masfParam'.$i;
       
  1343 }
       
  1344 
       
  1345 sub get_new_targetName
       
  1346 {
       
  1347     my ($host)=@_;
       
  1348     my ($i)=(0);
       
  1349     my ($try)=('masfTarget'.$i.$host);
       
  1350     while (targetName_exists($try) || length $try > 32) {
       
  1351 	if (length $try > 32) {
       
  1352 	    chop $host;
       
  1353 	    if (length $host == 0) {
       
  1354 		log_message "Unable to generate unique targetName\n";
       
  1355 		exit 1;
       
  1356 	    }
       
  1357 	} else {
       
  1358 	    $i++;
       
  1359 	}
       
  1360 	$try = 'masfTarget'.$i.$host;
       
  1361     }
       
  1362     return $try;
       
  1363 }
       
  1364 
       
  1365 sub paramName_exists
       
  1366 {
       
  1367     my ($paramName) = @_;
       
  1368     my ($keys) = ({'paramName'=>$paramName});
       
  1369     my (@lines) = (get_targetParams($keys, \%::SMA_CONFIGS),
       
  1370 	get_targetParams($keys, \%::MASF_CONFIGS),
       
  1371 	get_targetAddrs($keys, \%::SMA_CONFIGS),
       
  1372 	get_targetAddrs($keys, \%::MASF_CONFIGS));
       
  1373     if (@lines) {
       
  1374 	return 1;
       
  1375     } else {
       
  1376 	return 0;
       
  1377     }
       
  1378 }
       
  1379 
       
  1380 sub profileName_exists
       
  1381 {
       
  1382     my ($name) = @_;
       
  1383     my ($file, $line, $meta);
       
  1384     for $file (values %::MASF_CONFIGS, values %::SMA_CONFIGS) {
       
  1385 	for $line (@$file) {
       
  1386 	    $meta=$line->{'meta'};
       
  1387 	    if (exists $meta->{'profileName'} &&
       
  1388 		    $meta->{'profileName'} eq $name) {
       
  1389 		return 1;
       
  1390 	    }
       
  1391 	}
       
  1392     }
       
  1393     return 0;
       
  1394 }
       
  1395 
       
  1396 sub targetName_exists
       
  1397 {
       
  1398     my ($targetName) = @_;
       
  1399     my ($keys) = ({'targetName'=>$targetName});
       
  1400     my (@lines) = (get_targetAddrs($keys, \%::SMA_CONFIGS),
       
  1401 	get_targetAddrs($keys, \%::MASF_CONFIGS));
       
  1402     if (@lines) {
       
  1403 	return 1;
       
  1404     } else {
       
  1405 	return 0;
       
  1406     }
       
  1407 }
       
  1408 
       
  1409 # get all target addresses with the specified keys
       
  1410 sub get_targetAddrs
       
  1411 {
       
  1412     my ($keys, $configs)=@_;
       
  1413     my ($f, $l, @targetAddrs);
       
  1414     for $f (values %$configs) {
       
  1415 	for $l (@$f) {
       
  1416 	    if (! exists $l->{'meta'}->{'directive'} ||
       
  1417 		    ($l->{'meta'}->{'directive'} ne 'targetAddr')) {
       
  1418 		next;
       
  1419 	    }
       
  1420 	    if (exists $keys->{'paramName'} && 
       
  1421 		    $l->{'meta'}->{'paramName'} ne $keys->{'paramName'}) {
       
  1422 		next;
       
  1423 	    }
       
  1424 	    if (exists $keys->{'TDomain'} && 
       
  1425 		    $l->{'meta'}->{'TDomain'} ne $keys->{'TDomain'})
       
  1426 	    {
       
  1427 		next;
       
  1428 	    }
       
  1429 	    if (exists $keys->{'TAddress'} && 
       
  1430 		    $l->{'meta'}->{'TAddress'} ne $keys->{'TAddress'})
       
  1431 	    {
       
  1432 		next;
       
  1433 	    }
       
  1434 	    if (exists $keys->{'targetName'} && 
       
  1435 		    $l->{'meta'}->{'targetName'} ne $keys->{'targetName'})
       
  1436 	    {
       
  1437 		next;
       
  1438 	    }
       
  1439 	    push @targetAddrs, $l;
       
  1440 	}
       
  1441     }
       
  1442     return @targetAddrs;
       
  1443 }
       
  1444 
       
  1445 # get all target parameters with the specified keys
       
  1446 sub get_targetParams
       
  1447 {
       
  1448     my ($keys, $configs)=@_;
       
  1449     my ($f, $l, @targetParams);
       
  1450     for $f (values %$configs) {
       
  1451 	for $l (@$f) {
       
  1452 	    if (! exists $l->{'meta'}->{'directive'} ||
       
  1453 		    ($l->{'meta'}->{'directive'} ne 'targetParams')) {
       
  1454 		next;
       
  1455 	    }
       
  1456 	    if (exists $keys->{'paramName'} && 
       
  1457 		    $l->{'meta'}->{'paramName'} ne $keys->{'paramName'}) {
       
  1458 		next;
       
  1459 	    }
       
  1460 	    if (exists $keys->{'securityModel'} && 
       
  1461 		    $l->{'meta'}->{'securityModel'} ne $keys->{'securityModel'})
       
  1462 	    {
       
  1463 		next;
       
  1464 	    }
       
  1465 	    if (exists $keys->{'securityLevel'} && 
       
  1466 		    $l->{'meta'}->{'securityLevel'} ne $keys->{'securityLevel'})
       
  1467 	    {
       
  1468 		next;
       
  1469 	    }
       
  1470 	    if (exists $keys->{'securityName'} && 
       
  1471 		    $l->{'meta'}->{'securityName'} ne $keys->{'securityName'})
       
  1472 	    {
       
  1473 		next;
       
  1474 	    }
       
  1475 	    push @targetParams, $l;
       
  1476 	}
       
  1477     }
       
  1478     return @targetParams;
       
  1479 }
       
  1480 
       
  1481 sub process_trapsink
       
  1482 {
       
  1483     my ($sinkLine, $informTag, $trapTag) = @_;
       
  1484     my ($paramName, @paramLines, $community, @result, $secModel,
       
  1485 	$MPModel, $notifyType, %profiles, $foundParams);
       
  1486     log_message "Migrating trapsink: ".$sinkLine->{'new'}."\n";
       
  1487     if ($sinkLine->{'meta'}->{'directive'} eq 'trapsink') {
       
  1488 	$secModel = $SECURITY_MODEL_SNMPV1;
       
  1489 	$MPModel = $MPMODEL_SNMPV1;
       
  1490     } elsif ($sinkLine->{'meta'}->{'directive'} eq 'trap2sink' ||
       
  1491 	    $sinkLine->{'meta'}->{'directive'} eq 'informsink') {
       
  1492 	$secModel = $SECURITY_MODEL_SNMPV2C;
       
  1493 	$MPModel = $MPMODEL_SNMPV2C;
       
  1494     } 
       
  1495     if ($sinkLine->{'meta'}->{'directive'} eq 'informsink') {
       
  1496 	$notifyType = $NOTIFY_TYPE_INFORM;
       
  1497     } else {
       
  1498 	$notifyType = $NOTIFY_TYPE_TRAP;
       
  1499     }
       
  1500     $community = $sinkLine->{'meta'}->{'community'};
       
  1501 
       
  1502     # try to find an existing target param in SMA config but don't bother if we
       
  1503     # don't care about overlap
       
  1504     if ($::EXTEND_SMA_FILTERS) {
       
  1505     @paramLines = (get_targetParams({'securityName'=>$community,
       
  1506 		'securityModel'=>$SECURITY_MODEL_SNMPV2C}, \%::SMA_CONFIGS),
       
  1507 	    get_targetParams({'securityName'=>$community,
       
  1508 		'securityModel'=>$SECURITY_MODEL_SNMPV1}, \%::SMA_CONFIGS));
       
  1509     }
       
  1510 
       
  1511     my ($smaParamLine, $smaTargetLine);
       
  1512     my ($targetName, @targetAddrs);
       
  1513     my ($host) = $sinkLine->{'meta'}->{'host'};
       
  1514     my ($port) = $sinkLine->{'meta'}->{'port'};
       
  1515 
       
  1516     if (@paramLines) {
       
  1517 	my ($paramLine);
       
  1518 	for $paramLine (@paramLines) {
       
  1519 	    $paramName = $paramLine->{'meta'}->{'paramName'};
       
  1520 	    push @targetAddrs, get_targetAddrs({'TDomain'=>$SNMP_UDP_DOMAIN,
       
  1521 		    'TAddress'=>hostnameToUDPDomain($host.':'.$port),
       
  1522 		    'paramName'=>$paramName}, \%::SMA_CONFIGS)
       
  1523 	}
       
  1524 	
       
  1525 	# try to use an existing targetAddr if it was defined
       
  1526 	if (@targetAddrs > 0) {
       
  1527 	    $smaTargetLine = $targetAddrs[0];
       
  1528 	    ($smaParamLine) = get_targetParams({'paramName'=>$smaTargetLine->{'meta'}->{'paramName'}}, \%::SMA_CONFIGS);
       
  1529 	    $foundParams = 1;
       
  1530 	} 
       
  1531     }
       
  1532 
       
  1533     if (! $foundParams || ! $::KEEP_SNMP_TARGET_PARAMS) {
       
  1534 	# we need to generate a new set of params
       
  1535 	$paramName = get_new_paramName();
       
  1536 	push @result, {'meta'=>{'directive'=>'targetParams',
       
  1537 	    'paramName'=>$paramName,
       
  1538 	    'MPModel'=>$MPModel,
       
  1539 	    'securityModel'=>$secModel,
       
  1540 	    'securityName'=>$community,
       
  1541 	    'securityLevel'=>$SECURITY_LEVEL_AUTHNOPRIV,
       
  1542 	    'storageType'=>$DEFAULT_STORAGE_TYPE,
       
  1543 	    'rowStatus'=>$DEFAULT_ROW_STATUS}
       
  1544 	};
       
  1545     }
       
  1546 
       
  1547     my (@profiles, $profileName);
       
  1548 
       
  1549     if (! $foundParams) {
       
  1550 	# no equivalent SMA target found - only MASF traps to be delivered
       
  1551 	# generate our own targetAddr
       
  1552 	my ($i) = (0);
       
  1553 	$targetName = get_new_targetName($host);
       
  1554 	push @result, {'meta'=>{'directive'=>'targetAddr',
       
  1555 	    'targetName'=>$targetName,
       
  1556 	    'TDomain'=>$SNMP_UDP_DOMAIN,
       
  1557 	    'TAddress'=>hostnameToUDPDomain($host.':'.$port),
       
  1558 	    'timeout'=>1500,
       
  1559 	    'retryCount'=>3,
       
  1560 	    'tagList'=>$notifyType == $NOTIFY_TYPE_INFORM ? $informTag :
       
  1561 		$trapTag,
       
  1562 	    'paramName'=>$paramName,
       
  1563 	    'storageType'=>$DEFAULT_STORAGE_TYPE,
       
  1564 	    'rowStatus'=>$DEFAULT_ROW_STATUS}};
       
  1565 	# need to add a filter profile
       
  1566 	    $profileName = get_new_profileName();
       
  1567     } elsif (! $::KEEP_SNMP_TARGET_PARAMS) {
       
  1568 	# we need to change the targetAddr to refer to the new set of params 
       
  1569 	$targetName = $smaTargetLine->{'meta'}->{'targetName'};
       
  1570 	$smaTargetLine->{'meta'}->{'paramName'} = $paramName;
       
  1571 	$smaTargetLine->{'meta'}->{'tagList'} =
       
  1572 	    ($notifyType == $NOTIFY_TYPE_INFORM ? $informTag : $trapTag);
       
  1573 	replace_line(\%::SMA_CONFIGS, $smaTargetLine, $smaTargetLine);
       
  1574 
       
  1575 	# set our params to use the new set of filter profiles
       
  1576 	@profiles = get_filterProfiles(
       
  1577 		{'paramName'=>$smaParamLine->{'meta'}->{'paramName'}},
       
  1578 		\%::SMA_CONFIGS);
       
  1579 	if (@profiles > 0) {
       
  1580 	    $profileName = $profiles[0]->{'meta'}->{'profileName'};
       
  1581 	} else {
       
  1582 	    # no filter profiles defined
       
  1583 	    return @result;
       
  1584 	}
       
  1585     } else {
       
  1586 	# don't change existing target specification
       
  1587 	# SMA profiles already have MASF added.
       
  1588 	return @result;
       
  1589     }
       
  1590 
       
  1591     push @result, {'meta'=>{'directive'=>'snmpNotifyFilterProfileTable',
       
  1592 	'paramName'=>$paramName,
       
  1593 	'profileName'=>$profileName,
       
  1594 	'storageType'=>$DEFAULT_STORAGE_TYPE,
       
  1595 	'rowStatus'=>$DEFAULT_ROW_STATUS}
       
  1596     };
       
  1597 
       
  1598     if ($foundParams) {
       
  1599 	return @result;
       
  1600     }
       
  1601 
       
  1602     # add some filters
       
  1603     push @result, 
       
  1604     {'comment'=>'# Include ENTITY-MIB and SUN-PLATFORM-MIB in profile',
       
  1605 	'meta'=>{'directive'=>'snmpNotifyFilterTable',
       
  1606 	    'profileName'=>$profileName,
       
  1607 	    'subtree'=>$ENTITY_MIB_OID,
       
  1608 	    'mask'=>'',
       
  1609 	    'filterType'=>$FILTER_TYPE_INCLUDED,
       
  1610 	    'storageType'=>$DEFAULT_STORAGE_TYPE,
       
  1611 	    'rowStatus'=>$DEFAULT_ROW_STATUS
       
  1612 	}},
       
  1613 	{'meta'=>{'directive'=>'snmpNotifyFilterTable',
       
  1614 		     'profileName'=>$profileName,
       
  1615 		     'subtree'=>$SUNPLAT_MIB_OID,
       
  1616 		     'mask'=>'',
       
  1617 		     'filterType'=>$FILTER_TYPE_INCLUDED,
       
  1618 		     'storageType'=>$DEFAULT_STORAGE_TYPE,
       
  1619 		     'rowStatus'=>$DEFAULT_ROW_STATUS
       
  1620 		 }};	
       
  1621     return @result;
       
  1622 }
       
  1623 
       
  1624 sub process_trapsinks
       
  1625 {
       
  1626     # grab all the trapsink, trap2sink and informsink lines in MASF
       
  1627     my (@sinkLines) = (get_lines('trapsink', \%::MASF_CONFIGS),
       
  1628 	get_lines('trap2sink', \%::MASF_CONFIGS),
       
  1629 	get_lines('informsink', \%::MASF_CONFIGS));
       
  1630     my ($sinkLine, $paramName, @paramLines, $community, @result, $secModel,
       
  1631 	$MPModel, $notifyType, $informTag, $trapTag, %profiles, $foundParams);
       
  1632     %profiles=();
       
  1633     foreach $sinkLine (@sinkLines) {
       
  1634 	$paramName = undef;
       
  1635 	@result = ();
       
  1636 	$foundParams = 0;
       
  1637 	log_message "Processing trapsink: ".$sinkLine->{'new'}."\n";
       
  1638 	if (!defined $informTag) {
       
  1639 	    $informTag = generate_notify_tags('masfInformTag');
       
  1640 	    log_message "Using tag ".$informTag." for informs\n";
       
  1641 	    $trapTag = generate_notify_tags('masfTrapTag');
       
  1642 	    log_message "Using tag ".$trapTag." for traps\n";
       
  1643 	    push @result, {'meta'=>{'directive'=>'snmpNotifyTable',
       
  1644 		'notifyName'=>$informTag,
       
  1645 		'notifyTag'=>$informTag,
       
  1646 		'notifyType'=>$NOTIFY_TYPE_INFORM,
       
  1647 		'storageType'=>$DEFAULT_STORAGE_TYPE,
       
  1648 		'rowStatus'=>$DEFAULT_ROW_STATUS
       
  1649 	    }},
       
  1650 	    {'meta'=>{'directive'=>'snmpNotifyTable',
       
  1651 			 'notifyName'=>$trapTag,
       
  1652 			 'notifyTag'=>$trapTag,
       
  1653 			 'notifyType'=>$NOTIFY_TYPE_TRAP,
       
  1654 			 'storageType'=>$DEFAULT_STORAGE_TYPE,
       
  1655 			 'rowStatus'=>$DEFAULT_ROW_STATUS
       
  1656 		     }};
       
  1657 	}
       
  1658 	push @result, process_trapsink($sinkLine, $informTag, $trapTag);
       
  1659 	replace_line(\%::MASF_CONFIGS, $sinkLine, @result);
       
  1660     }
       
  1661 }
       
  1662 
       
  1663 sub extend_sma_trap_filters
       
  1664 {
       
  1665     my (@lines, $line);
       
  1666     @lines = get_lines('snmpNotifyFilterTable', \%::SMA_CONFIGS);
       
  1667     my (%hash);
       
  1668     foreach $line (@lines) {
       
  1669 	if (!exists $hash{$line->{'meta'}->{'profileName'}}) {
       
  1670 	    log_message "Adding ENTITY-MIB and SUN-PLATFORM-MIB traps to ".
       
  1671 	    "trap filter profile ".$line->{'meta'}->{'profileName'}."\n";
       
  1672 	    $hash{$line->{'meta'}->{'profileName'}}=undef;
       
  1673 	    insert_lines(\%::SMA_CONFIGS, $line, 0,
       
  1674 		{'comment'=>'# Include ENTITY-MIB and SUN-PLATFORM-MIB in profile',
       
  1675 		'meta'=>{'directive'=>'snmpNotifyFilterTable',
       
  1676 		    'profileName'=>$line->{'meta'}->{'profileName'},
       
  1677 		    'subtree'=>$ENTITY_MIB_OID,
       
  1678 		    'mask'=>'',
       
  1679 		    'filterType'=>$FILTER_TYPE_INCLUDED,
       
  1680 		    'storageType'=>$DEFAULT_STORAGE_TYPE,
       
  1681 		    'rowStatus'=>$DEFAULT_ROW_STATUS}},
       
  1682 		{'meta'=>{'directive'=>'snmpNotifyFilterTable',
       
  1683 		    'profileName'=>$line->{'meta'}->{'profileName'},
       
  1684 		    'subtree'=>$SUNPLAT_MIB_OID,
       
  1685 		    'mask'=>'',
       
  1686 		    'filterType'=>$FILTER_TYPE_INCLUDED,
       
  1687 		    'storageType'=>$DEFAULT_STORAGE_TYPE,
       
  1688 		    'rowStatus'=>$DEFAULT_ROW_STATUS}});
       
  1689 	}	
       
  1690     }
       
  1691 }
       
  1692 
       
  1693 sub process_trapcommunity
       
  1694 {
       
  1695     my ($configs)=@_;
       
  1696     my ($i, $file, $trapcommunity, $meta);
       
  1697     $trapcommunity = 'public';
       
  1698     foreach $file (values %$configs) {
       
  1699 	for ($i = 0; $i < @$file; $i++) {
       
  1700 	    $meta = $file->[$i]->{'meta'};
       
  1701 	    if (! exists $file->[$i]->{'meta'}->{'directive'}) {
       
  1702 		next;
       
  1703 	    }
       
  1704 	    if ($meta->{'directive'} eq 'trapcommunity') {
       
  1705 		$trapcommunity = $meta->{'community'};
       
  1706 		replace_line($configs, $file->[$i], {'meta'=>{}});
       
  1707 		next;
       
  1708 	    }
       
  1709 	    if ($meta->{'directive'} eq 'trapsink' ||
       
  1710 		    $meta->{'directive'} eq 'trap2sink' ||
       
  1711 		    $meta->{'directive'} eq 'informsink') {
       
  1712 		if (! exists $meta->{'community'}) {
       
  1713 			$meta->{'community'} = $trapcommunity;
       
  1714 			replace_line($configs, $file->[$i], $file->[$i]);
       
  1715 		}
       
  1716 		if (! exists $meta->{'port'}) {
       
  1717 		    $meta->{'port'} = 162;
       
  1718 		    replace_line($configs, $file->[$i], $file->[$i]);
       
  1719 		}
       
  1720 	    }
       
  1721 	}
       
  1722     }
       
  1723 }
       
  1724 
       
  1725 sub check_duplicate_trap_destinations 
       
  1726 {
       
  1727     my (@smaTraps) = (get_lines('informsink', \%::SMA_CONFIGS),
       
  1728 	get_lines('trapsink', \%::SMA_CONFIGS),
       
  1729 	get_lines('trap2sink', \%::SMA_CONFIGS));
       
  1730     my (@masfTraps) = (get_lines('informsink', \%::MASF_CONFIGS),
       
  1731 	get_lines('trapsink', \%::MASF_CONFIGS),
       
  1732 	get_lines('trap2sink', \%::MASF_CONFIGS));
       
  1733     my ($trap, @matches, $match);
       
  1734     for $trap (@masfTraps) {
       
  1735 	@matches = grep (
       
  1736 	    ($_->{'meta'}->{'community'} eq $trap->{'meta'}->{'community'} &&
       
  1737 	     $_->{'meta'}->{'host'} eq $trap->{'meta'}->{'host'} &&
       
  1738 	     $_->{'meta'}->{'port'} eq $trap->{'meta'}->{'port'}), @smaTraps);
       
  1739 	if (@matches) {
       
  1740 	    # remove this duplicate
       
  1741 	    log_message "Removing duplicate ".$trap->{'meta'}->{'directive'}." directive\n", $trap;
       
  1742 	    replace_line(\%::MASF_CONFIGS, $trap, {'meta'=>{}});
       
  1743 	}
       
  1744     }
       
  1745 }
       
  1746 
       
  1747 ##############################################################################
       
  1748 # Access control, groups and users
       
  1749 
       
  1750 $::SEEDED = 0;
       
  1751 
       
  1752 sub remove_v3_users
       
  1753 {
       
  1754     my (@users) = get_securityNames({'securityModel'=>'usm'}, \%::MASF_CONFIGS);
       
  1755     my ($user, @groups, $group);
       
  1756     for $user (@users) {
       
  1757 	replace_line(\%::MASF_CONFIGS, $user, {'meta'=>{}});
       
  1758 	@groups = get_groups($user->{'meta'}, \%::MASF_CONFIGS);
       
  1759 	for $group (@groups) {
       
  1760 	    replace_line(\%::MASF_CONFIGS, $group, {'meta'=>{}});
       
  1761 	}
       
  1762     }
       
  1763 }
       
  1764 
       
  1765 sub remove_v1v2c_users
       
  1766 {
       
  1767     my (@users) = get_securityNames({'securityModel'=>'v1'}, \%::MASF_CONFIGS);
       
  1768     my ($user, @groups, $group);
       
  1769     for $user (@users) {
       
  1770 	replace_line(\%::MASF_CONFIGS, $user, {'meta'=>{}});
       
  1771 	@groups = get_groups($user->{'meta'}, \%::MASF_CONFIGS);
       
  1772 	for $group (@groups) {
       
  1773 	    replace_line(\%::MASF_CONFIGS, $group, {'meta'=>{}});
       
  1774 	}
       
  1775     }
       
  1776 }
       
  1777 
       
  1778 sub generate_password
       
  1779 {
       
  1780     my ($i, $r, $pwd);
       
  1781     if (! $::SEEDED) {
       
  1782 	srand;
       
  1783 	$::SEEDED=1;
       
  1784     }
       
  1785 
       
  1786     for ($i = 0; $i < 8; $i++) {
       
  1787 	do {
       
  1788 	    $r = chr (int (rand 127));
       
  1789 	} while ($r!~/^[a-zA-Z0-9]$/);
       
  1790 	$pwd.=$r;
       
  1791     }
       
  1792     return $pwd;
       
  1793 }
       
  1794 
       
  1795 # check to see whether MASF UsmUser directives can be transferred without
       
  1796 # alteration, if not then convert to a template createUser statement
       
  1797 sub process_engineIDs
       
  1798 {
       
  1799     log_message "\n";
       
  1800     log_message "Checking to see if USM users defined in usmUser directives can be migrated without modification:\n";
       
  1801     my ($lines, $line, $can_migrate);
       
  1802     $can_migrate = 1;
       
  1803     # check that no v3 users have been specified in SMA
       
  1804     my (@v3_sma_users) = (get_securityNames({'securityModel'=>'usm'}, \%::SMA_CONFIGS));
       
  1805     if (@v3_sma_users > 0) {
       
  1806 	log_message "V3 users were specified in SMA config - cannot migrate without modification.\n";
       
  1807 	$can_migrate = 0;
       
  1808     }
       
  1809     # If the engineID was specified in MASF then we cannot migrate. Remove it
       
  1810     # from the migrated configuration
       
  1811     for $line (get_lines('engineID', \%::MASF_CONFIGS)) {
       
  1812 	$can_migrate = 0;
       
  1813 	log_message "engineID specified in configuration file - cannot migrate without modification.\n", $line;
       
  1814 	replace_line(\%::MASF_CONFIGS, $line, {'meta'=>{}});
       
  1815     }
       
  1816     # check that engineID isn't specified anywhere in SMA config
       
  1817     for $line (get_lines('engineID', \%::SMA_CONFIGS)) {
       
  1818 	log_message "engineID specified in configuration file - cannot migrate without modification.\n", $line;
       
  1819 	$can_migrate = 0;
       
  1820     }
       
  1821 
       
  1822     if (! $can_migrate) {
       
  1823 	for $line (get_lines('usmUser', \%::MASF_CONFIGS)) {
       
  1824 	    if (! $::IGNORE_ENGINEID) {
       
  1825 		log_message "User with securityName ".
       
  1826 		$line->{'meta'}->{'securityName'}." cannot be migrated ".
       
  1827 		"because the engineID has changed - aborting.\n", $line;
       
  1828 		exit 1;
       
  1829 	    }
       
  1830 	    log_message "\n";
       
  1831 	    log_message "***************************************************************************\n";
       
  1832 	    log_message 'User with securityName '.
       
  1833 	    $line->{'meta'}->{'securityName'}." has had their password reset.\n";
       
  1834 	    log_message "You will need to edit the configuration file after ".
       
  1835 	    "this script has finished\n".
       
  1836 		"in order to enable their account\n";
       
  1837 	    log_message "***************************************************************************\n";
       
  1838 	    replace_line(\%::MASF_CONFIGS, $line,
       
  1839 		    {'comment'=>'# XXX Change the password if necessary',
       
  1840 		    'meta'=>{'directive'=>'createUser',
       
  1841 		    'securityModel'=>'usm',
       
  1842 		    'securityName'=>$line->{'meta'}->{'securityName'},
       
  1843 		    'authPassword'=>generate_password(),
       
  1844 		    'authProtocol'=>'MD5'}});
       
  1845 	}
       
  1846 	# if we can't migrate the engineID, then remove any oldEngineID
       
  1847 	# statements and engineBoots statements
       
  1848 	for $line (get_lines('oldEngineID', \%::MASF_CONFIGS),
       
  1849 		get_lines('engineBoots', \%::MASF_CONFIGS)) {
       
  1850 	    replace_line(\%::MASF_CONFIGS, $line, {'meta'=>{}});
       
  1851 	}
       
  1852 	return;
       
  1853     } else {
       
  1854 	log_message "OK to migrate usm users\n";
       
  1855     }
       
  1856 
       
  1857     # check to see if oldEngineID or engineBoots is defined in SMA config - if
       
  1858     # so then squash it in favour of new one from MASF
       
  1859     for $line (get_lines('oldEngineID', \%::SMA_CONFIGS),
       
  1860 	    get_lines('engineBoots', \%::SMA_CONFIGS)) {
       
  1861 	replace_line(\%::SMA_CONFIGS, $line, {'meta'=>{}});
       
  1862     }
       
  1863 }
       
  1864 
       
  1865 # ensure that if a createUser and usmUser directive is present for the same
       
  1866 # securityName then the createUser directive takes precedence
       
  1867 sub check_usm_securityNames
       
  1868 {
       
  1869     my (@masfUserLines)=(get_securityNames({'securityModel'=>'usm'},
       
  1870 	\%::MASF_CONFIGS));
       
  1871     my (@smaUserLines)=(get_securityNames({'securityModel'=>'usm'},
       
  1872 	\%::SMA_CONFIGS));
       
  1873     my ($usmUser, $user, $config);
       
  1874     for $config (\%::MASF_CONFIGS, \%::SMA_CONFIGS) {
       
  1875 	my (@userLines) = (get_securityNames({'securityModel'=>'usm'},
       
  1876 	$config));
       
  1877 	my (@createUsers) = (grep 'createUser' eq $_->{'meta'}->{'directive'}, @userLines);
       
  1878 	for $user (@createUsers) {
       
  1879 	    my (@usmUsers) = (grep (('usmUser' eq $_->{'meta'}->{'directive'}
       
  1880 		&& $user->{'meta'}->{'securityName'} eq
       
  1881 		$_->{'meta'}->{'securityName'}), @userLines));
       
  1882 	    for $usmUser (@usmUsers) {
       
  1883 		replace_line($config, $usmUser, {'meta'=>{}});
       
  1884 	    }
       
  1885 	}
       
  1886     }
       
  1887 }
       
  1888 
       
  1889 sub choose_group_membership
       
  1890 {
       
  1891 	my ($smaSecurityName, $masfSecurityName) = @_;
       
  1892 	# interactive mode
       
  1893 	convert_metadata();
       
  1894 	log_message "\nThis user is defined in SMA with the following configuration:\n";
       
  1895 	# get the config:
       
  1896 	my ($o, $n) = get_old_new_config($smaSecurityName,
       
  1897 			\%::SMA_CONFIGS);
       
  1898 
       
  1899 	log_message "Original configuration:\n".join ("\n",@$o);
       
  1900 	log_message "\n\nProposed configuration 1:\n".join ("\n",@$n);
       
  1901 	($o, $n) = get_old_new_config($masfSecurityName, \%::MASF_CONFIGS);
       
  1902 	log_message "\n\nIt conflicts with the following MASF configuration:\nOriginal configuration:\n".join ("\n", @$o);
       
  1903 	log_message "\n\nProposed configuration 2:\n".join ("\n",@$n)."\n";
       
  1904 	my ($response) = (prompt ("Please choose one of the".
       
  1905 				" proposed configurations", 1, [1,2]));
       
  1906 	if ($response == 1) {
       
  1907 		remove_com2sec_user ({'securityName'=>$masfSecurityName}, \%::MASF_CONFIGS);
       
  1908 	} else {
       
  1909 		remove_com2sec_user ({'securityName'=>$smaSecurityName}, \%::SMA_CONFIGS);
       
  1910 	}
       
  1911 }
       
  1912 
       
  1913 sub process_usm_securityNames
       
  1914 {
       
  1915     my ($file, $line, $fileb, $lineb, $snameLine, $sname, $conflicted);
       
  1916     for $snameLine (get_securityNames({'securityModel'=>'usm'}, \%::MASF_CONFIGS)) {
       
  1917 	$sname = $snameLine->{'meta'}->{'securityName'};
       
  1918 	$conflicted = undef;
       
  1919 	for $file (values %::SMA_CONFIGS) {
       
  1920 	    for $line (@$file) {
       
  1921 		if (exists $line->{'meta'}->{'securityName'} &&
       
  1922 			$line->{'meta'}->{'securityName'} eq $sname) {
       
  1923 		    $conflicted=$line;
       
  1924 		    last;
       
  1925 		}
       
  1926 	    }
       
  1927 	}
       
  1928 	if (defined $conflicted) {
       
  1929 	    # there is a clash
       
  1930 	    log_message "MASF securityName ".$sname." conflicts with one already defined in SMA\n", $snameLine;
       
  1931 	    if ($::KEEP_SMA_USM_USERS) {
       
  1932 		remove_com2sec_user($snameLine->{'meta'}, \%::MASF_CONFIGS);
       
  1933 	    } elsif ($::KEEP_MASF_USM_USERS) {
       
  1934 		remove_com2sec_user($conflicted->{'meta'}, \%::SMA_CONFIGS);
       
  1935 	    } elsif ($::AUTOMATED) {
       
  1936 		log_message "Conflicting securityNames found - aborting.  Please resolve these conflicts\n".
       
  1937 		"manually.\n";
       
  1938 		exit 1;
       
  1939 	    } else {
       
  1940 		choose_group_membership($conflicted->{'meta'}->{'securityName'}, $sname);
       
  1941 	    }
       
  1942 	}
       
  1943     }
       
  1944 }
       
  1945 
       
  1946 sub remove_com2sec_user
       
  1947 {
       
  1948     my ($keys, $config)=@_;
       
  1949     my ($securityName)=($keys->{'securityName'});
       
  1950     my ($f, $l);
       
  1951     for $l (get_securityNames($keys, $config)) {
       
  1952 	replace_line ($config, $l, {'meta'=>{}});
       
  1953     }
       
  1954     for $f (values %$config) {
       
  1955 	for $l (@$f) {
       
  1956 	    if (exists $l->{'meta'}->{'directive'} &&
       
  1957 		    $l->{'meta'}->{'directive'} eq 'group' &&
       
  1958 		    $l->{'meta'}->{'securityName'} eq $securityName) {
       
  1959 		replace_line($config, $l, {'meta'=>{}});
       
  1960 	    }
       
  1961 	}
       
  1962     }
       
  1963 }
       
  1964 
       
  1965 # get all lines which define a security name i.e. com2sec, createUser or
       
  1966 # usmUser, according to specified keys
       
  1967 sub get_securityNames
       
  1968 {
       
  1969 	my ($keys, $configs)=@_;
       
  1970 	my ($f, $l, @securityNames);
       
  1971 	for $f (values %$configs) {
       
  1972 		for $l (@$f) {
       
  1973 			if (! exists $l->{'meta'}->{'directive'} ||
       
  1974 					($l->{'meta'}->{'directive'} ne 'createUser' &&
       
  1975 					$l->{'meta'}->{'directive'} ne 'usmUser' &&
       
  1976 					$l->{'meta'}->{'directive'} ne 'com2sec')) {
       
  1977 				next;
       
  1978 			}
       
  1979 			if (exists $keys->{'securityName'} && 
       
  1980 					$l->{'meta'}->{'securityName'} ne $keys->{'securityName'}) {
       
  1981 				next;
       
  1982 			}
       
  1983 			if (exists $keys->{'securityModel'}) {
       
  1984 				my ($sm) = ('usm'); 
       
  1985 				if ($keys->{'securityModel'} eq 'v2c' || 
       
  1986 					$keys->{'securityModel'} eq 'v1') {
       
  1987 					$sm = 'v1';
       
  1988 				}
       
  1989 				if (($sm eq 'v1' &&
       
  1990 				$l->{'meta'}->{'directive'} ne 'com2sec') ||
       
  1991 				($sm eq 'usm' &&
       
  1992 				($l->{'meta'}->{'directive'} ne 'createUser' &&
       
  1993 				$l->{'meta'}->{'directive'} ne 'usmUser'))) {
       
  1994 					next;
       
  1995 				}
       
  1996 			}
       
  1997 			push @securityNames, $l;
       
  1998 		}
       
  1999 	}
       
  2000 	return @securityNames;
       
  2001 }
       
  2002 
       
  2003 sub get_groups
       
  2004 {
       
  2005 	my ($keys, $configs)=@_;
       
  2006 	my ($f, $l, @groups);
       
  2007 	for $f (values %$configs) {
       
  2008 		for $l (@$f) {
       
  2009 			if (! exists $l->{'meta'}->{'directive'} ||
       
  2010 					$l->{'meta'}->{'directive'} ne 'group') {
       
  2011 				next;
       
  2012 			}
       
  2013 			if (exists $keys->{'securityName'} && 
       
  2014 					$l->{'meta'}->{'securityName'} ne $keys->{'securityName'}) {
       
  2015 				next;
       
  2016 			}
       
  2017 			if (exists $keys->{'securityModel'} &&
       
  2018 					$l->{'meta'}->{'securityModel'} ne $keys->{'securityModel'}) {
       
  2019 				next;
       
  2020 			}
       
  2021 			if (exists $keys->{'groupName'} &&
       
  2022 				$l->{'meta'}->{'groupName'} ne $keys->{'groupName'}) {
       
  2023 				next;
       
  2024 			}
       
  2025 			push @groups, $l
       
  2026 		}
       
  2027 	}
       
  2028 	return @groups;
       
  2029 }
       
  2030 
       
  2031 sub get_access {
       
  2032 	my ($keys, $configs)=@_;
       
  2033 	my ($f, $l, @access);
       
  2034 	for $f (values %$configs) {
       
  2035 		for $l (@$f) {
       
  2036 			if (! exists $l->{'meta'}->{'directive'} ||
       
  2037 					$l->{'meta'}->{'directive'} ne 'access') {
       
  2038 				next;
       
  2039 			}
       
  2040 			if (exists $keys->{'groupName'} && 
       
  2041 					$l->{'meta'}->{'groupName'} ne $keys->{'groupName'}) {
       
  2042 				next;
       
  2043 			}
       
  2044 			if (exists $keys->{'viewName'} &&
       
  2045 				($l->{'meta'}->{'readView'} ne $keys->{'viewName'} &&
       
  2046 				$l->{'meta'}->{'writeView'} ne $keys->{'viewName'} &&
       
  2047 				$l->{'meta'}->{'notifyView'} ne $keys->{'viewName'})) {
       
  2048 				next;
       
  2049 			}
       
  2050 			push @access, $l
       
  2051 		}
       
  2052 	}
       
  2053 	return @access;
       
  2054 }
       
  2055 
       
  2056 sub get_views {
       
  2057 	my ($keys, $configs)=@_;
       
  2058 	my ($f, $l, @views);
       
  2059 	if (exists $keys->{'viewName'} &&
       
  2060 		$keys->{'viewName'} eq '') {
       
  2061 		return ();
       
  2062 	}
       
  2063 	for $f (values %$configs) {
       
  2064 		for $l (@$f) {
       
  2065 			if (! exists $l->{'meta'}->{'directive'} ||
       
  2066 					$l->{'meta'}->{'directive'} ne 'view') {
       
  2067 				next;
       
  2068 			}
       
  2069 			if (exists $keys->{'viewName'} && 
       
  2070 					$l->{'meta'}->{'viewName'} ne $keys->{'viewName'}) {
       
  2071 				next;
       
  2072 			}
       
  2073 			push @views, $l
       
  2074 		}
       
  2075 	}
       
  2076 	return @views;
       
  2077 }
       
  2078 
       
  2079 sub get_old_new_config
       
  2080 {
       
  2081 	my ($securityName, $configs) = @_;
       
  2082 	my ($line, @secNames, @groups, $group, $access, @accesses, @views, @old, @new);
       
  2083 	my (@a, $keys);
       
  2084 	$keys = {'securityName'=>$securityName};
       
  2085 	@secNames = get_securityNames($keys, $configs);
       
  2086 	@groups = get_groups ({'securityName'=>$securityName}, $configs);
       
  2087 	# get other members of this group
       
  2088 	if (@groups > 0) {
       
  2089 		@groups = set_union([get_groups({'groupName'=>$groups[0]->{'meta'}->{'groupName'}}, $configs)], \@groups);
       
  2090 	}
       
  2091 	for $group (@groups) {
       
  2092 		(@accesses) = (set_union(\@accesses, [get_access($group->{'meta'}, $configs)]));
       
  2093 	}
       
  2094 	for $access (@accesses) {
       
  2095 		$keys = {'viewName'=>$access->{'meta'}->{'readView'}};
       
  2096 		(@views) = (set_union(\@views, [get_views($keys, $configs)]));
       
  2097 		$keys->{'viewName'}=$access->{'meta'}->{'writeView'};
       
  2098 		(@views) = (set_union(\@views, [get_views($keys, $configs)]));
       
  2099 		$keys->{'viewName'}=$access->{'meta'}->{'notifyView'};
       
  2100 		(@views) = (set_union(\@views, [get_views($keys, $configs)]));
       
  2101 	}
       
  2102 	for $line (@secNames, @groups, @accesses, @views) {
       
  2103 		if (exists $line->{'orig'}) {
       
  2104 			push @old, $line->{'orig'};
       
  2105 		} 
       
  2106 		if (exists $line->{'new'}) {
       
  2107 			push @new, $line->{'new'};
       
  2108 		}
       
  2109 	}
       
  2110 	return [@old], [@new];
       
  2111 }
       
  2112 
       
  2113 sub parse_source
       
  2114 {
       
  2115 	my ($line) = @_;
       
  2116 	my ($source) = $line->{'meta'}->{'source'};
       
  2117 	my ($hostIP, $mask, $ipSegment);
       
  2118 	# was it 'default'
       
  2119 	if ($source eq 'default' || $source eq '0.0.0.0') {
       
  2120 		return (inet_aton('0.0.0.0'), inet_aton('0.0.0.0'));
       
  2121 	}
       
  2122 	# try to resolve as a plain IP address or a hostname
       
  2123 	$ipSegment=$source;
       
  2124 	($ipSegment, $mask) = split /\//, $source;
       
  2125 	# try to resolve hostname as an IP address
       
  2126 	my ($hostent) = gethostbyname($ipSegment);
       
  2127 	if (defined $hostent) {
       
  2128 		if ($hostent->addrtype ne AF_INET) {
       
  2129 			log_message "Unsupported address family in source $source\n", $line;
       
  2130 			exit 1;
       
  2131 		}
       
  2132 		my (@hostIPs) = @{$hostent->addr_list};
       
  2133 		log_message "resolved $source to ".inet_ntoa($hostIPs[0])."\n", $line;
       
  2134 		$hostIP = $hostIPs[0];
       
  2135 	} else {
       
  2136 		log_message "Couldn't resolve $ipSegment as a hostname or IP address\n", $line;
       
  2137 		exit 1;
       
  2138 	}
       
  2139 	# try to resolve as subnet/mask
       
  2140 	if (defined $mask && $mask ne '') {
       
  2141 		my $netmask;
       
  2142 		$netmask = inet_aton($mask);
       
  2143 		if (! defined $netmask) {
       
  2144 
       
  2145 			# try to interpret as number between 0 and 32
       
  2146 			my ($bit) = (0x80000000);
       
  2147 			$netmask = 0;
       
  2148 			if ($mask < 0 || $mask > 32) {
       
  2149 				log_message "$source did not contain a valid netmask.\n", $line;
       
  2150 				exit 1;
       
  2151 			}
       
  2152 			while ($mask > 0) {
       
  2153 				$netmask |= $bit;
       
  2154 				$bit = $bit >> 1;
       
  2155 			}
       
  2156 		}
       
  2157 		my ($N, $H) = (in_addr_to_number ($netmask), in_addr_to_number
       
  2158 			($hostIP));
       
  2159 		if ($H & ~$N) {
       
  2160 			log_message("source/mask mismatch - ".inet_ntoa($hostIP).'/'.inet_ntoa($netmask)."\n", $line);
       
  2161 			exit 1;
       
  2162 		}
       
  2163 		return ($hostIP, $netmask);
       
  2164 	} else {
       
  2165 		# no netmask specified - exact match
       
  2166 		return ($hostIP, inet_aton('255.255.255.255'));
       
  2167 	}
       
  2168 }
       
  2169 		
       
  2170 sub source_overlap
       
  2171 {
       
  2172 	my ($linea, $lineb) = @_;
       
  2173 	my ($ipa, $maska) = parse_source($linea);
       
  2174 	my ($ipb, $maskb) = parse_source($lineb);
       
  2175 	my $mask_overlap;
       
  2176 	$ipa = in_addr_to_number($ipa);
       
  2177 	$ipb = in_addr_to_number($ipb);
       
  2178 	$mask_overlap = in_addr_to_number($maska) & in_addr_to_number($maskb);
       
  2179 	my $complement = 0xffffffff & (~$mask_overlap);
       
  2180 	if (($ipa & $mask_overlap | $complement) == ($ipb & $mask_overlap | $complement)) {
       
  2181 		return 1;
       
  2182 	}
       
  2183 	return 0;
       
  2184 }
       
  2185 
       
  2186 sub community_conflicts
       
  2187 {
       
  2188 	my ($linea)=@_;
       
  2189 	my ($community, $source) = ($linea->{'meta'}->{'community'}, $linea->{'meta'}->{'source'});
       
  2190 	my ($lines, $line);
       
  2191 	for $lines (values %::SMA_CONFIGS) {
       
  2192 		for $line (@$lines) {
       
  2193 			if (! exists $line->{'meta'}->{'community'} ||
       
  2194 				$line->{'meta'}->{'community'} ne $community) {
       
  2195 				next;
       
  2196 			}
       
  2197 			if (! exists $line->{'meta'}->{'source'}) {
       
  2198 				next;
       
  2199 			}
       
  2200 			if (source_overlap ($line, $linea)) {
       
  2201 				return ($line);
       
  2202 			}
       
  2203 		}
       
  2204 	}
       
  2205 	return undef;
       
  2206 }
       
  2207 
       
  2208 sub process_com2sec
       
  2209 {
       
  2210     my ($lines, $line);
       
  2211     my $conflict;
       
  2212     for $lines (values %::MASF_CONFIGS) {
       
  2213 	for $line (@$lines) {
       
  2214 	    if (! exists $line->{'meta'}->{'directive'} ||
       
  2215 		    $line->{'meta'}->{'directive'} ne 'com2sec') {
       
  2216 		next;
       
  2217 	    }
       
  2218 
       
  2219 	    $conflict = community_conflicts($line);
       
  2220 	    if (defined $conflict) {
       
  2221 		log_message "MASF source ".$line->{'meta'}->{'source'}.
       
  2222 		    " conflicts with SMA source ".$conflict->{'meta'}->{'source'}.
       
  2223 		    " defined for community ".$line->{'meta'}->{'community'}."\n", $line;
       
  2224 		if ($::KEEP_SMA_GROUPS) {
       
  2225 		    # remove user from MASF config
       
  2226 		    remove_com2sec_user ($line->{'meta'}, \%::MASF_CONFIGS);
       
  2227 		} elsif ($::KEEP_MASF_GROUPS) {
       
  2228 		    # remove user from SMA config
       
  2229 		    remove_com2sec_user ($line->{'meta'}, \%::SMA_CONFIGS);
       
  2230 		} elsif ($::AUTOMATED) {
       
  2231 		    # didn't define what to do so stop!
       
  2232 		    log_message "Conflicting source/communities found - ".
       
  2233 		    "aborting. Please resolve these conflicts manually.\n";
       
  2234 		    exit 1;
       
  2235 		} else {
       
  2236 		    choose_group_membership($conflict->{'meta'}->{'securityName'}, $line->{'meta'}->{'securityName'});
       
  2237 		}
       
  2238 	    }
       
  2239 	}
       
  2240     }
       
  2241 }
       
  2242 
       
  2243 sub uniquify_viewNames
       
  2244 {
       
  2245     my ($prefix) = @_;
       
  2246     my %mappings=();
       
  2247     my ($lines, $line, $securityName, $i, @keys, $key, $from);
       
  2248     @keys = ('viewName', 'readView', 'writeView', 'notifyView');
       
  2249     for $lines (values %::MASF_CONFIGS) {
       
  2250 	foreach $line (@$lines) {
       
  2251 	    for $key (@keys) {
       
  2252 		if (exists $line->{'meta'}->{$key} &&
       
  2253 			$line->{'meta'}->{$key} ne '') {
       
  2254 		    $from = $line->{'meta'}->{$key};
       
  2255 		} else {
       
  2256 		    next;
       
  2257 		}
       
  2258 
       
  2259 		# is there already a mapping for this securityName ?
       
  2260 		if (exists $mappings{$from}) {
       
  2261 		    $line->{'meta'}->{$key} = $mappings{$from};
       
  2262 		    replace_line(\%::MASF_CONFIGS, $line, $line);
       
  2263 		    next;
       
  2264 		}
       
  2265 
       
  2266 		# generate a new securityName
       
  2267 		$i = 0;
       
  2268 		while (viewName_exists($prefix.$i.$from))
       
  2269 		{
       
  2270 		    $i++;
       
  2271 		}
       
  2272 		if (length $prefix.$i.$from > 32) {
       
  2273 		    log_message ("Couldn't render viewName $from unique\n", $line);
       
  2274 		    exit 1;
       
  2275 		}
       
  2276 		$mappings{$from}=$prefix.$i.$from;
       
  2277 		$line->{'meta'}->{$key} = $prefix.$i.$from;
       
  2278 		replace_line(\%::MASF_CONFIGS, $line, $line);
       
  2279 	    }
       
  2280 	}
       
  2281     }
       
  2282     # remove redundant view names
       
  2283     my (@viewLines, @accessLines, $view, $config);
       
  2284     for $config (\%::MASF_CONFIGS, \%::SMA_CONFIGS) {
       
  2285 	@viewLines = get_views({}, $config);
       
  2286 	for $view (@viewLines) {
       
  2287 	    @accessLines = (get_access($view->{'meta'}, \%::MASF_CONFIGS), get_access($view->{'meta'}, \%::SMA_CONFIGS));
       
  2288 	    if (@accessLines == 0) {
       
  2289 		replace_line($config, $view, {'meta'=>{}});
       
  2290 	    }
       
  2291 	}
       
  2292     }
       
  2293 }
       
  2294 
       
  2295 sub uniquify_groupNames
       
  2296 {
       
  2297     my ($prefix) = @_;
       
  2298     my %mappings=();
       
  2299     my ($lines, $line, $securityName, $i, $from);
       
  2300     for $lines (values %::MASF_CONFIGS) {
       
  2301 	foreach $line (@$lines) {
       
  2302 	    if (exists $line->{'meta'}->{'groupName'}) {
       
  2303 		$from = $line->{'meta'}->{'groupName'};
       
  2304 	    } else {
       
  2305 		next;
       
  2306 	    }
       
  2307 
       
  2308 	    # is there already a mapping for this securityName ?
       
  2309 	    if (exists $mappings{$from}) {
       
  2310 		$line->{'meta'}->{'groupName'} = $mappings{$from};
       
  2311 		replace_line(\%::MASF_CONFIGS, $line, $line);
       
  2312 		next;
       
  2313 	    }
       
  2314 
       
  2315 	    # generate a new securityName
       
  2316 	    $i = 0;
       
  2317 	    while (groupName_exists($prefix.$i.$from))
       
  2318 	    {
       
  2319 		$i++;
       
  2320 	    }
       
  2321 	    if (length $prefix.$i.$from > 32) {
       
  2322 		log_message ("Couldn't render groupName $from unique\n", $line);
       
  2323 		exit 1;
       
  2324 	    }
       
  2325 	    $mappings{$from}=$prefix.$i.$from;
       
  2326 	    $line->{'meta'}->{'groupName'} = $prefix.$i.$from;
       
  2327 	    replace_line(\%::MASF_CONFIGS, $line, $line);
       
  2328 	}
       
  2329     }
       
  2330 }
       
  2331 
       
  2332 # remove group directives referring to undefined users
       
  2333 sub clean_group_membership
       
  2334 {
       
  2335     my ($config);
       
  2336     for $config (\%::MASF_CONFIGS, \%::SMA_CONFIGS) {
       
  2337 	my (@groups) = get_lines('group', $config);
       
  2338 	my ($group);
       
  2339 	for $group (@groups) {
       
  2340 	    my (@securityNames) = get_securityNames($group->{'meta'}, $config);
       
  2341 	    if (@securityNames == 0) {
       
  2342 		# no securityName was defined for this directive - remove the
       
  2343 		# directive
       
  2344 		log_message "Removing undefined user ".
       
  2345 		$group->{'meta'}->{'securityName'}." from group ".
       
  2346 		$group->{'meta'}->{'groupName'}."\n", $group;
       
  2347 		replace_line($config, $group, {'meta'=>{}});
       
  2348 	    }
       
  2349 	}
       
  2350     }
       
  2351 }
       
  2352 
       
  2353 sub uniquify_securityNames
       
  2354 {
       
  2355     my ($prefix) = @_;
       
  2356     my ($to);
       
  2357     my ($lines, $line, $securityName, $i, $from, $fromLine);
       
  2358     for $fromLine (get_securityNames({'securityModel'=>'v1'},
       
  2359 		\%::MASF_CONFIGS)) {
       
  2360 	# generate a new securityName
       
  2361 	$from = $fromLine->{'meta'}->{'securityName'};
       
  2362 	$to = get_new_securityName($prefix.$from);
       
  2363 	for $lines (values %::MASF_CONFIGS) {
       
  2364 	    foreach $line (@$lines) {
       
  2365 		if (! exists $line->{'meta'}->{'securityName'} ||
       
  2366 			$line->{'meta'}->{'securityName'} ne $from) {
       
  2367 		    next;
       
  2368 		}
       
  2369 
       
  2370 		$line->{'meta'}->{'securityName'} = $to;
       
  2371 		replace_line(\%::MASF_CONFIGS, $line, $line);
       
  2372 	    }
       
  2373 	}
       
  2374     }
       
  2375 }
       
  2376 
       
  2377 sub check_tokens
       
  2378 {
       
  2379     my ($line, $minTokens, $maxTokens, @tokens) = @_;
       
  2380     my ($directive) = $line->{'meta'}->{'directive'};
       
  2381     if (@tokens < $minTokens || @tokens > $maxTokens) {
       
  2382 	log_message "Invalid number of parameters for $directive directive ".
       
  2383 	"- Aborting\n", $line;
       
  2384 	exit 1;
       
  2385     }
       
  2386 }
       
  2387 
       
  2388 # convert simple array of lines into hash containing data which is what we
       
  2389 # subsequently modify
       
  2390 sub add_metadata
       
  2391 {
       
  2392     my ($lines, $file, $line, $i, $s, $directive, @tokens);
       
  2393     my ($key, $j);
       
  2394     for $key (keys %::SMA_CONFIGS) {
       
  2395 	for ($i = 0; $i < @{$::SMA_CONFIGS{$key}}; $i++) {
       
  2396 	    $line = $::SMA_CONFIGS{$key}->[$i];
       
  2397 	    $::SMA_CONFIGS{$key}->[$i] = {'orig'=>$line,
       
  2398 		'new'=>$line,
       
  2399 		# NB the 'meta' hash doesn't contain metadata, only real data...
       
  2400 		'meta'=>{},
       
  2401 		'lineno'=>$i,
       
  2402 		'file'=>$key};
       
  2403 	}
       
  2404     }
       
  2405     for $key (keys %::MASF_CONFIGS) {
       
  2406 	for ($i = 0; $i < @{$::MASF_CONFIGS{$key}}; $i++) {
       
  2407 	    $line = $::MASF_CONFIGS{$key}->[$i];
       
  2408 	    $::MASF_CONFIGS{$key}->[$i] = {'orig'=>$line,
       
  2409 		'new'=>$line,
       
  2410 		'meta'=>{},
       
  2411 		'lineno'=>$i,
       
  2412 		'file'=>$key};
       
  2413 	}
       
  2414     }
       
  2415     for $lines (values %::SMA_CONFIGS, values %::MASF_CONFIGS) {
       
  2416 	for $line (@$lines) {
       
  2417 	    ($directive, @tokens) = parse_config_line($line->{'new'});
       
  2418 	    if ($directive ne '') {
       
  2419 		$line->{'meta'}->{'directive'} = $directive;
       
  2420 	    }
       
  2421 	    if ($directive eq 'com2sec') {
       
  2422 		check_tokens($line, 3, 3, @tokens);
       
  2423 		$line->{'meta'}->{'securityName'} = $tokens[0];
       
  2424 		$line->{'meta'}->{'source'} = $tokens[1];
       
  2425 		$line->{'meta'}->{'community'} = $tokens[2];
       
  2426 	    } elsif ($directive eq 'group') {
       
  2427 		check_tokens($line, 3, 3, @tokens);
       
  2428 		$line->{'meta'}->{'groupName'} = $tokens[0];
       
  2429 		$line->{'meta'}->{'securityModel'} = $tokens[1];
       
  2430 		$line->{'meta'}->{'securityName'} = $tokens[2];
       
  2431 	    } elsif ($directive eq 'rouser' || $directive eq 'rwuser') {
       
  2432 		check_tokens($line, 1, 3, @tokens);
       
  2433 		$line->{'meta'}->{'securityName'} = $tokens[0];
       
  2434 		if (defined $tokens[1]) {
       
  2435 		    $line->{'meta'}->{'securityLevel'} = $tokens[1];
       
  2436 		}
       
  2437 		if (defined $tokens[2]) {
       
  2438 		    $line->{'meta'}->{'oid'} = $tokens[2];
       
  2439 		}
       
  2440 	    } elsif ($directive eq 'createUser') {
       
  2441 		check_tokens($line, 3, 5, @tokens);
       
  2442 		$line->{'meta'}->{'securityName'} = $tokens[0];
       
  2443 		$line->{'meta'}->{'authProtocol'} = $tokens[1];
       
  2444 		$line->{'meta'}->{'authPassword'} = $tokens[2];
       
  2445 		$line->{'meta'}->{'securityModel'} = 'usm';
       
  2446 		if (defined $tokens[3]) {
       
  2447 		    $line->{'meta'}->{'privProtocol'} = $tokens[3];
       
  2448 		}
       
  2449 		if (defined $tokens[4]) {
       
  2450 		    $line->{'meta'}->{'privPassword'} = $tokens[4];
       
  2451 		}
       
  2452 	    } elsif ($directive eq 'usmUser') {
       
  2453 		check_tokens($line, 11, 11, @tokens);
       
  2454 		$line->{'meta'}->{'securityName'} = securityName_from_usmUser($directive, @tokens);
       
  2455 		$line->{'meta'}->{'securityModel'} = 'usm';
       
  2456 	    } elsif ($directive eq 'access') {
       
  2457 		check_tokens($line, 8, 8, @tokens);
       
  2458 		$line->{'meta'}->{'groupName'} = $tokens[0];
       
  2459 		$line->{'meta'}->{'contextPrefix'} = $tokens[1];
       
  2460 		$line->{'meta'}->{'securityModel'} = $tokens[2];
       
  2461 		$line->{'meta'}->{'securityLevel'} = $tokens[3];
       
  2462 		$line->{'meta'}->{'contextMatch'} = $tokens[4];
       
  2463 		$line->{'meta'}->{'readView'} = $tokens[5];
       
  2464 		$line->{'meta'}->{'writeView'} = $tokens[6];
       
  2465 		$line->{'meta'}->{'notifyView'} = $tokens[7];
       
  2466 	    } elsif ($directive eq 'view') {
       
  2467 		check_tokens($line, 3, 4, @tokens);
       
  2468 		$line->{'meta'}->{'viewName'} = $tokens[0];
       
  2469 		$line->{'meta'}->{'viewType'} = $tokens[1];
       
  2470 		$line->{'meta'}->{'oid'} = $tokens[2];
       
  2471 		if (defined $tokens[3]) {
       
  2472 		    $line->{'meta'}->{'mask'} = $tokens[3];
       
  2473 		}
       
  2474 	    } elsif ($directive eq 'rocommunity' || $directive eq 'rwcommunity') {
       
  2475 		check_tokens($line, 1, 3, @tokens);
       
  2476 		$line->{'meta'}->{'community'} = $tokens[0];
       
  2477 		if (defined $tokens[1]) {
       
  2478 		    $line->{'meta'}->{'source'} = $tokens[1];
       
  2479 		} else {
       
  2480 		    $line->{'meta'}->{'source'} = "0.0.0.0/0.0.0.0";
       
  2481 		}
       
  2482 		if (defined $tokens[2]) {
       
  2483 		    $line->{'meta'}->{'oid'} = $tokens[2];
       
  2484 		}
       
  2485 	    } elsif ($directive eq 'trapcommunity') {
       
  2486 		check_tokens($line, 1, 1, @tokens);
       
  2487 		$line->{'meta'}->{'community'} = $tokens[0];
       
  2488 	    } elsif ($directive eq 'trapsink' ||
       
  2489 		    $directive eq 'trap2sink' ||
       
  2490 		    $directive eq 'informsink') {
       
  2491 		check_tokens($line, 1, 3, @tokens);
       
  2492 		$line->{'meta'}->{'host'} = $tokens[0];
       
  2493 		if (defined $tokens[1]) {
       
  2494 		    $line->{'meta'}->{'community'} = $tokens[1];
       
  2495 		}
       
  2496 		if (defined $tokens[2]) {
       
  2497 		    $line->{'meta'}->{'port'} = $tokens[2];
       
  2498 		}
       
  2499 	    } elsif ($directive eq 'snmpNotifyFilterTable') {
       
  2500 		check_tokens($line, 6, 6, @tokens);
       
  2501 		$line->{'meta'}->{'profileName'} = $tokens[0];
       
  2502 		$line->{'meta'}->{'subtree'} = $tokens[1];
       
  2503 		$line->{'meta'}->{'mask'} = $tokens[2];
       
  2504 		$line->{'meta'}->{'filterType'} = $tokens[3];
       
  2505 		$line->{'meta'}->{'storageType'} = $tokens[4];
       
  2506 		$line->{'meta'}->{'rowStatus'} = $tokens[5];
       
  2507 	    } elsif ($directive eq 'targetParams') {
       
  2508 		check_tokens($line, 7, 7, @tokens);
       
  2509 		$line->{'meta'}->{'paramName'} = $tokens[0];
       
  2510 		$line->{'meta'}->{'MPModel'} = $tokens[1];
       
  2511 		$line->{'meta'}->{'securityModel'} = $tokens[2];
       
  2512 		$line->{'meta'}->{'securityName'} = $tokens[3];
       
  2513 		$line->{'meta'}->{'securityLevel'} = $tokens[4];
       
  2514 		$line->{'meta'}->{'storageType'} = $tokens[5];
       
  2515 		$line->{'meta'}->{'rowStatus'} = $tokens[6];
       
  2516 	    } elsif ($directive eq 'targetAddr') {
       
  2517 		check_tokens($line, 9, 9, @tokens);
       
  2518 		$line->{'meta'}->{'targetName'} = $tokens[0];
       
  2519 		$line->{'meta'}->{'TDomain'} = $tokens[1];
       
  2520 		$line->{'meta'}->{'TAddress'} = $tokens[2];
       
  2521 		$line->{'meta'}->{'timeout'} = $tokens[3];
       
  2522 		$line->{'meta'}->{'retryCount'} = $tokens[4];
       
  2523 		$line->{'meta'}->{'tagList'} = $tokens[5];
       
  2524 		$line->{'meta'}->{'paramName'} = $tokens[6];
       
  2525 		$line->{'meta'}->{'storageType'} = $tokens[7];
       
  2526 		$line->{'meta'}->{'rowStatus'} = $tokens[8];
       
  2527 	    } elsif ($directive eq 'snmpNotifyTable') {
       
  2528 		check_tokens($line, 5, 5, @tokens);
       
  2529 		$line->{'meta'}->{'notifyName'} = $tokens[0];
       
  2530 		$line->{'meta'}->{'notifyTag'} = $tokens[1];
       
  2531 		$line->{'meta'}->{'notifyType'} = $tokens[2];
       
  2532 		$line->{'meta'}->{'storageType'} = $tokens[3];
       
  2533 		$line->{'meta'}->{'rowStatus'} = $tokens[4];
       
  2534 	    } elsif ($directive eq 'snmpNotifyFilterProfileTable') {
       
  2535 		check_tokens($line, 4, 4, @tokens);
       
  2536 		$line->{'meta'}->{'paramName'} = $tokens[0];
       
  2537 		$line->{'meta'}->{'profileName'} = $tokens[1];
       
  2538 		$line->{'meta'}->{'storageType'} = $tokens[2];
       
  2539 		$line->{'meta'}->{'rowStatus'} = $tokens[3];
       
  2540 	    } elsif ($directive eq 'master') {
       
  2541 		check_tokens($line, 1, 1, @tokens);
       
  2542 	    	$line->{'meta'}->{'masterMode'} = $tokens[0];
       
  2543 	    } elsif ($directive eq 'agentxTimeout') {
       
  2544 		check_tokens($line, 1, 1, @tokens);
       
  2545 		$line->{'meta'}->{'agentxTimeout'} = $tokens[0];
       
  2546 	    } elsif ($directive eq 'agentxRetries') {
       
  2547 		check_tokens($line, 1, 1, @tokens);
       
  2548 		$line->{'meta'}->{'agentxRetries'} = $tokens[0];
       
  2549 	    }
       
  2550 	}
       
  2551     }
       
  2552 }
       
  2553 
       
  2554 sub convert_metadata
       
  2555 {
       
  2556     my ($line, $file, $meta, $directive);
       
  2557     my %mps = ($MPMODEL_SNMPV1=>'v1',
       
  2558 	    $MPMODEL_SNMPV2C=>'v2c',
       
  2559 	    $MPMODEL_SNMPV2U=>'v2u',
       
  2560 	    $MPMODEL_SNMPV3=>'v3');
       
  2561     my %sms = ($SECURITY_MODEL_ANY=>'any',
       
  2562 	    $SECURITY_MODEL_SNMPV1=>'v1',
       
  2563 	    $SECURITY_MODEL_SNMPV2C=>'v2c',
       
  2564 	    $SECURITY_MODEL_USM=>'usm');
       
  2565     my %sls = ($SECURITY_LEVEL_NOAUTHNOPRIV=>'noAuthNoPriv',
       
  2566 	    $SECURITY_LEVEL_AUTHNOPRIV=>'authNoPriv',
       
  2567 	    $SECURITY_LEVEL_AUTHPRIV=>'authPriv');
       
  2568     my %nts = ($NOTIFY_TYPE_TRAP=>'trap',
       
  2569 	    $NOTIFY_TYPE_INFORM=>'inform');
       
  2570     for $file (values %::ADDED_CONFIGS, values %::MASF_CONFIGS, values
       
  2571 	    %::SMA_CONFIGS) {
       
  2572 	for $line (@$file) {
       
  2573 	    $meta = $line->{'meta'};
       
  2574 	    if (exists $meta->{'directive'} &&
       
  2575 		    exists $line->{'changed'}) {
       
  2576 		delete $line->{'changed'};
       
  2577 		$directive = $meta->{'directive'};
       
  2578 		if ($directive eq 'com2sec') {
       
  2579 		    # don't quote securityName or community
       
  2580 		    $line->{'new'} = "com2sec ".$meta->{'securityName'}.
       
  2581 			' '.$meta->{'source'}.' '.$meta->{'community'};
       
  2582 		} elsif ($directive eq 'group') {
       
  2583 		    # don't quote groupName
       
  2584 		    $line->{'new'} = 'group '.$meta->{'groupName'}.
       
  2585 			' '.$meta->{'securityModel'}.' '.
       
  2586 			# don't quote securityName
       
  2587 			$meta->{'securityName'}.'';
       
  2588 		} elsif ($directive eq 'access') {
       
  2589 		my ($readView, $writeView, $notifyView) =
       
  2590 		    map { $_ eq '' ? 'none' : $_ }
       
  2591 		($meta->{'readView'}, $meta->{'writeView'},
       
  2592 		 $meta->{'notifyView'});
       
  2593 		# don't quote groupName, or viewNames 
       
  2594 		$line->{'new'} = 'access '.$meta->{'groupName'}.
       
  2595 		    ' "'.$meta->{'contextPrefix'}.'" '.
       
  2596 		    $meta->{'securityModel'}.' '.$meta->{'securityLevel'}.
       
  2597 		    ' '.$meta->{'contextMatch'}.' '.$readView.
       
  2598 		    ' '.$writeView.' '.$notifyView;
       
  2599 		} elsif ($directive eq 'view') {
       
  2600 		# don't quote viewName
       
  2601 		    $line->{'new'} = 'view '.$meta->{'viewName'}.' '.
       
  2602 			$meta->{'viewType'}.' '.$meta->{'oid'};
       
  2603 		    if (exists $meta->{'mask'}) {
       
  2604 			$line->{'new'}.=' '.$meta->{'mask'};
       
  2605 		    }
       
  2606 		} elsif ($directive eq 'createUser') {
       
  2607 		    $line->{'new'} = 'createUser "'.$meta->{'securityName'}.
       
  2608 			'" '.$meta->{'authProtocol'}.' "'.$meta->{'authPassword'}.
       
  2609 			'"';
       
  2610 		    if (exists $meta->{'privProtocol'}) {
       
  2611 			$line->{'new'}.=' '.$meta->{'privProtocol'};
       
  2612 		    }
       
  2613 		    if (exists $meta->{'privPassword'}) {
       
  2614 			$line->{'new'}.=' "'.$meta->{'privPassword'}.'" ';
       
  2615 		    }
       
  2616 		} elsif ($directive eq 'trap2sink' ||
       
  2617 			$directive eq 'trapsink' ||
       
  2618 			$directive eq 'informsink') {
       
  2619 		    $line->{'new'} = $directive.' '.$meta->{'host'};
       
  2620 		    if (exists $meta->{'community'}) {
       
  2621 			# don't quote community
       
  2622 			$line->{'new'}.=' '.$meta->{'community'}.'';
       
  2623 		    }
       
  2624 		    if (exists $meta->{'port'}) {
       
  2625 			$line->{'new'}.=' '.$meta->{'port'};
       
  2626 		    }
       
  2627 		} elsif ($directive eq 'snmpNotifyFilterTable') {
       
  2628 		    $line->{'new'} = 'snmpNotifyFilterTable "'.
       
  2629 			$meta->{'profileName'}.'" '.$meta->{'subtree'}.' "'.
       
  2630 			$meta->{'mask'}.'" '.$meta->{'filterType'}.' '.
       
  2631 			$meta->{'storageType'}.' '.$meta->{'rowStatus'};
       
  2632 		} elsif ($directive eq 'targetParams') {
       
  2633 		    $line->{'comment'} = '# targetParams '.$meta->{'paramName'}.
       
  2634 		    ' '.$mps{$meta->{'MPModel'}}.' '.
       
  2635 		    $sms{$meta->{'securityModel'}}.' '.$meta->{'securityName'}.
       
  2636 		    ' '.$sls{$meta->{'securityLevel'}};
       
  2637 		    $line->{'new'} = 'targetParams "'.$meta->{'paramName'}.'" '.
       
  2638 		    $meta->{'MPModel'}.' '.$meta->{'securityModel'}.' "'.
       
  2639 		    $meta->{'securityName'}.'" '.$meta->{'securityLevel'}.' '.
       
  2640 		    $meta->{'storageType'}.' '.$meta->{'rowStatus'};
       
  2641 		} elsif ($directive eq 'targetAddr') {
       
  2642 		    $line->{'new'} = 'targetAddr "'.$meta->{'targetName'}.
       
  2643 			'" '.$meta->{'TDomain'}.' '.$meta->{'TAddress'}.
       
  2644 			' '.$meta->{'timeout'}.' '.$meta->{'retryCount'}.
       
  2645 			' "'.$meta->{'tagList'}.'" "'.$meta->{'paramName'}.
       
  2646 			'" '.$meta->{'storageType'}.' '.$meta->{'rowStatus'};
       
  2647 		} elsif ($directive eq 'snmpNotifyTable') {
       
  2648 		    $line->{'comment'} = '# snmpNotifyTable '.$meta->{'notifyName'}.
       
  2649 		    ' '.$meta->{'notifyTag'}.' '.$nts{$meta->{'notifyType'}};
       
  2650 		    $line->{'new'} = 'snmpNotifyTable "'.$meta->{'notifyName'}.
       
  2651 		    '" "'.$meta->{'notifyTag'}.'" '.$meta->{'notifyType'}.
       
  2652 		    ' '.$meta->{'storageType'}.' '.$meta->{'rowStatus'};
       
  2653 		} elsif ($directive eq 'snmpNotifyFilterProfileTable') {
       
  2654 		    $line->{'new'} = 'snmpNotifyFilterProfileTable "'.
       
  2655 			$meta->{'paramName'}.'" "'.$meta->{'profileName'}.
       
  2656 			'" '.$meta->{'storageType'}.' '.$meta->{'rowStatus'};
       
  2657 		} elsif ($directive eq 'master') {
       
  2658 		    $line->{'new'} = 'master '.$meta->{'masterMode'};
       
  2659 		} elsif ($directive eq 'agentxTimeout') {
       
  2660 		    $line->{'new'} = 'agentxTimeout '.$meta->{'agentxTimeout'};
       
  2661 		} elsif ($directive eq 'agentxRetries') {
       
  2662 		    $line->{'new'} = 'agentxRetries '.$meta->{'agentxRetries'};
       
  2663 		}
       
  2664 	    }
       
  2665 	}
       
  2666     }
       
  2667 }
       
  2668 
       
  2669 
       
  2670 # convert all rwuser directives to rouser
       
  2671 sub convert_rwusers
       
  2672 {
       
  2673     my ($line, $file);
       
  2674     my ($directive, @tokens);
       
  2675     for $line (get_lines('rwuser', \%::MASF_CONFIGS)) {
       
  2676 	$line->{'meta'}->{'directive'} = 'rouser';
       
  2677 	replace_line(\%::MASF_CONFIGS, $line, $line);
       
  2678     }
       
  2679 }
       
  2680 
       
  2681 # convert all rwcommunity directives to rocommunity
       
  2682 sub convert_rwcommunities
       
  2683 {
       
  2684     my ($line);
       
  2685     for $line (get_lines('rwcommunity', \%::MASF_CONFIGS)) {
       
  2686 	$line->{'meta'}->{'directive'} = 'rocommunity';
       
  2687 	replace_line(\%::MASF_CONFIGS, $line, $line);
       
  2688     }
       
  2689 }
       
  2690 
       
  2691 sub securityName_from_usmUser
       
  2692 {
       
  2693     my ($directive, @tokens) = @_;
       
  2694     my ($string);
       
  2695     $tokens[3]=~s/^0x//;
       
  2696     $string = pack 'H*', $tokens[3];
       
  2697     return unpack 'Z*', $string;
       
  2698 }
       
  2699 
       
  2700 sub viewName_exists
       
  2701 {
       
  2702     my ($viewName) = @_;
       
  2703     my ($file, $line, $meta);
       
  2704     for $file (values %::MASF_CONFIGS, values %::SMA_CONFIGS) {
       
  2705 	for $line (@$file) {
       
  2706 	    $meta=$line->{'meta'};
       
  2707 	    if ((exists $meta->{'readView'} &&
       
  2708 			$meta->{'readView'} eq $viewName) ||
       
  2709 		    (exists $meta->{'writeView'} &&
       
  2710 		     $meta->{'writeView'} eq $viewName) ||
       
  2711 		    (exists $meta->{'notifyView'} &&
       
  2712 		     $meta->{'notifyView'} eq $viewName) ||
       
  2713 		    (exists $meta->{'viewName'} &&
       
  2714 		     $meta->{'viewName'} eq $viewName)) {
       
  2715 		return 1;
       
  2716 	    }
       
  2717 	}
       
  2718     }
       
  2719     return 0;
       
  2720 }
       
  2721 
       
  2722 sub get_new_viewName
       
  2723 {
       
  2724     my ($prefix)=@_;
       
  2725     my ($viewName, $i);
       
  2726     $i = 0;
       
  2727 
       
  2728     do {
       
  2729 	$viewName = $prefix.$i;
       
  2730 	if (length $viewName > 32) {
       
  2731 	    log_message("viewName $viewName was longer than 32 characters\n");
       
  2732 	    exit 1;
       
  2733 	}
       
  2734 	$i++;
       
  2735     } while (viewName_exists($viewName));
       
  2736     return $viewName;
       
  2737 }
       
  2738 
       
  2739 sub groupName_exists
       
  2740 {
       
  2741     my ($groupName) = @_;
       
  2742     my ($file, $line, $meta);
       
  2743     for $file (values %::MASF_CONFIGS, values %::SMA_CONFIGS) {
       
  2744 	for $line (@$file) {
       
  2745 	    $meta=$line->{'meta'};
       
  2746 	    if (exists $meta->{'groupName'} &&
       
  2747 		    $meta->{'groupName'} eq $groupName) {
       
  2748 		return 1;
       
  2749 	    }
       
  2750 	}
       
  2751     }
       
  2752     return 0;
       
  2753 }
       
  2754 
       
  2755 sub get_new_groupName
       
  2756 {
       
  2757     my ($prefix)=@_;
       
  2758     my ($groupName, $i);
       
  2759     $i = 0;
       
  2760 
       
  2761     do {
       
  2762 	$groupName = $prefix.$i;
       
  2763 	if (length $groupName > 32) {
       
  2764 	    log_message("groupName $groupName was longer than 32 characters\n");
       
  2765 	    exit 1;
       
  2766 	}
       
  2767 	$i++;
       
  2768     } while (groupName_exists($groupName));
       
  2769     return $groupName;
       
  2770 }
       
  2771 
       
  2772 sub securityName_exists
       
  2773 {
       
  2774     my ($securityName) = @_;
       
  2775     my ($file, $line, $meta);
       
  2776     for $file (values %::MASF_CONFIGS, values %::SMA_CONFIGS) {
       
  2777 	for $line (@$file) {
       
  2778 	    $meta=$line->{'meta'};
       
  2779 
       
  2780 	    if (exists $meta->{'securityName'} &&
       
  2781 		    $meta->{'securityName'} eq $securityName) {
       
  2782 		return 1;
       
  2783 	    }
       
  2784 	}
       
  2785     }
       
  2786     return 0;
       
  2787 }
       
  2788 
       
  2789 sub get_new_securityName
       
  2790 {
       
  2791     my ($prefix)=@_;
       
  2792     my ($securityName, $i);
       
  2793     $i = 0;
       
  2794 
       
  2795     do {
       
  2796 	$securityName = $prefix.$i;
       
  2797 	# check to see if length is more than 32 characters permitted
       
  2798 	# by VACM
       
  2799 	if (length $securityName > 32) {
       
  2800 	    log_message("securityName $securityName was longer".
       
  2801 		    " than 32 characters\n");
       
  2802 	    exit 1;
       
  2803 	}
       
  2804 
       
  2805 	$i++;
       
  2806     } while (securityName_exists($securityName));
       
  2807     return $securityName;
       
  2808 }	
       
  2809 
       
  2810 sub expand_rouser
       
  2811 {
       
  2812     my ($line, $default_oids) = @_;
       
  2813     my ($securityName, $groupName, $viewName, $secLevel, $oid);
       
  2814     my (@output, $readView, $writeView, $i);
       
  2815     @output = ($line);
       
  2816     $securityName = $line->{'meta'}->{'securityName'};
       
  2817     if (! defined $line->{'meta'}->{'securityLevel'}) {
       
  2818     	$secLevel = "auth";
       
  2819     } else {
       
  2820 	$secLevel = $line->{'meta'}->{'securityLevel'};
       
  2821     }
       
  2822     if (! defined $line->{'meta'}->{'oid'}) {
       
  2823     	$oid = '';
       
  2824     } else {
       
  2825 	$oid = $line->{'meta'}->{'oid'};
       
  2826     }
       
  2827 
       
  2828     $groupName = get_new_groupName("Group");
       
  2829     $viewName = get_new_viewName("View");
       
  2830 
       
  2831     if ($line->{'meta'}->{'directive'} eq 'rouser') {
       
  2832 	$readView = $viewName;
       
  2833 	$writeView = '';
       
  2834     } else {
       
  2835 	$readView = $viewName;
       
  2836 	$writeView = $viewName;
       
  2837     }
       
  2838 
       
  2839     $output[0]->{'meta'} = {'directive'=>'group',
       
  2840 	'groupName'=>$groupName,
       
  2841 	'securityModel'=>'usm',
       
  2842 	'securityName'=>$securityName};
       
  2843     push @output, {'meta'=>{'directive'=>'access',
       
  2844 	'groupName'=>$groupName,
       
  2845 	'contextPrefix'=>'',
       
  2846 	'securityModel'=>'usm',
       
  2847 	'securityLevel'=>$secLevel,
       
  2848 	'contextMatch'=>'exact',
       
  2849 	'readView'=>$readView,
       
  2850 	'writeView'=>$writeView,
       
  2851 	'notifyView'=>''}
       
  2852     };
       
  2853     if ($oid ne '') {
       
  2854 	push @output, {'meta'=>{'directive'=>'view',
       
  2855 	    'viewName'=>$viewName,
       
  2856 	    'viewType'=>'included',
       
  2857 	    'oid'=>$oid}};
       
  2858     } else {
       
  2859 	for ($i = 0; $i < @$default_oids; $i++) {
       
  2860 	    push @output,{'meta'=>{'directive'=>'view',
       
  2861 		'viewName'=>$viewName,
       
  2862 		'viewType'=>'included',
       
  2863 		'oid'=>$default_oids->[$i]}};
       
  2864 	}
       
  2865     }
       
  2866     return @output;
       
  2867 }
       
  2868 
       
  2869 sub expand_rocommunity
       
  2870 {
       
  2871     my ($line, $default_oids) = @_;
       
  2872     my ($securityName, $groupName, $viewName, $community, $source, $oid);
       
  2873     my (@output, $readView, $writeView, $i);
       
  2874     @output = ($line);
       
  2875     $community = $line->{'meta'}->{'community'};
       
  2876     $source = $line->{'meta'}->{'source'};
       
  2877     if (defined $line->{'meta'}->{'oid'}) {
       
  2878 	$oid = $line->{'meta'}->{'oid'};
       
  2879     } else {
       
  2880 	$oid = '';
       
  2881     }
       
  2882     	
       
  2883     $securityName = get_new_securityName("User");
       
  2884     $groupName = get_new_groupName("Group");
       
  2885     $viewName = get_new_viewName("View");
       
  2886 
       
  2887     if ($line->{'meta'}->{'directive'} eq 'rocommunity') {
       
  2888 		$readView = $viewName;
       
  2889 		$writeView = '';
       
  2890     } else { 
       
  2891 		$readView = $viewName;
       
  2892 		$writeView = $viewName;
       
  2893     }
       
  2894 
       
  2895     $output[0]->{'meta'} = {'directive'=>"com2sec",
       
  2896 	'securityName'=>$securityName,
       
  2897 	'source'=>$source,
       
  2898 	'community'=>$community
       
  2899     };
       
  2900     push @output, {'meta'=>{'directive'=>'group',
       
  2901 	'groupName'=>$groupName,
       
  2902 	'securityModel'=>'v1',
       
  2903 	'securityName'=>$securityName}
       
  2904     },
       
  2905     {'meta'=>{'directive'=>'group',
       
  2906 		 'groupName'=>$groupName,
       
  2907 		 'securityModel'=>'v2c',
       
  2908 		 'securityName'=>$securityName}
       
  2909     },
       
  2910     {'meta'=>{'directive'=>'access',
       
  2911 		 'groupName'=>$groupName,
       
  2912 		 'contextPrefix'=>'',
       
  2913 		 'securityModel'=>'v1',
       
  2914 		 'securityLevel'=>'noauth',
       
  2915 		 'contextMatch'=>'exact',
       
  2916 		 'readView'=>$readView,
       
  2917 		 'writeView'=>$writeView,
       
  2918 		'notifyView'=>''}
       
  2919     },
       
  2920     {'meta'=>{'directive'=>'access',
       
  2921 		 'groupName'=>$groupName,
       
  2922 		 'contextPrefix'=>'',
       
  2923 		 'securityModel'=>'v2c',
       
  2924 		 'securityLevel'=>'noauth',
       
  2925 		 'contextMatch'=>'exact',
       
  2926 		 'readView'=>$readView,
       
  2927 		 'writeView'=>$writeView,
       
  2928 		 'notifyView'=>''}
       
  2929     };
       
  2930     if ($oid ne '') {
       
  2931 	push @output, {'meta'=>{'directive'=>'view',
       
  2932 	    'viewName'=>$viewName,
       
  2933 	    'viewType'=>'included',
       
  2934 	    'oid'=>$oid}};
       
  2935     } else {
       
  2936 	for ($i = 0; $i < @$default_oids; $i++) {
       
  2937 	    push @output, {'meta'=>{'directive'=>'view',
       
  2938 		'viewName'=>$viewName,
       
  2939 		'viewType'=>'included',
       
  2940 		'oid'=>$default_oids->[$i]}};
       
  2941 	}
       
  2942     }
       
  2943     return @output;
       
  2944 }
       
  2945 
       
  2946 sub expand_rousers
       
  2947 {
       
  2948     my ($configs, $default_oids) = @_;
       
  2949     my ($file, $lines, $i, $output, $line, $directive, @tokens);
       
  2950     my (@expanded);
       
  2951     for $file (keys %$configs) {
       
  2952 	$lines = $configs->{$file};
       
  2953 	$output=[];
       
  2954 	for ($i = 0; $i < @$lines; $i++) {
       
  2955 	    $line = $lines->[$i];
       
  2956 	    if (! exists $line->{'meta'}->{'directive'}) {
       
  2957 		next;
       
  2958 	    }
       
  2959 	    $directive = $line->{'meta'}->{'directive'};
       
  2960 	    if ($directive eq 'rouser' || $directive eq 'rwuser') {
       
  2961 		# expand rocommunity directive
       
  2962 		@expanded = expand_rouser($line, $default_oids);
       
  2963 		splice @$lines, $i, 1, @expanded;
       
  2964 		$i += $#expanded;
       
  2965 	    }
       
  2966 	}
       
  2967     }
       
  2968 }
       
  2969 
       
  2970 sub expand_rocommunities
       
  2971 {
       
  2972     my ($configs, $default_oids)= @_;
       
  2973     my ($file, $lines, $i, $output, $line, $directive, @tokens);
       
  2974     my (@expanded);
       
  2975     for $file (keys %$configs) {
       
  2976 	$lines = $configs->{$file};
       
  2977 	$output=[];
       
  2978 	for ($i = 0; $i < @$lines; $i++) {
       
  2979 	    $line = $lines->[$i];
       
  2980 	    if (! exists $line->{'meta'}->{'directive'}) {
       
  2981 		next;
       
  2982 	    }
       
  2983 
       
  2984 	    if ($line->{'meta'}->{'directive'} eq 'rocommunity' ||
       
  2985 		    $line->{'meta'}->{'directive'} eq 'rwcommunity') {
       
  2986 		# expand rocommunity directive
       
  2987 		@expanded = expand_rocommunity($line, $default_oids);
       
  2988 		splice @$lines, $i, 1, @expanded;
       
  2989 		$i += $#expanded;
       
  2990 	    }
       
  2991 	}
       
  2992     }
       
  2993 }
       
  2994 
       
  2995 ##############################################################################
       
  2996 # General configuration
       
  2997 
       
  2998 sub install_agentx_config
       
  2999 {
       
  3000     my (@lines) = get_lines('master', \%::SMA_CONFIGS);
       
  3001     if (@lines) {
       
  3002 	if (grep (($lines[0]->{'meta'}->{'masterMode'} eq $_),
       
  3003 		    ('agentx', 'all', 'yes', 'on'))) {
       
  3004 	    # master mode is enabled anyway
       
  3005 	} else {
       
  3006 	    # master mode was disabled!
       
  3007 	    log_message "AgentX mastering capability has been explicitly ".
       
  3008 	    "disabled in the SMA config - Aborting.\n", $lines[0];
       
  3009 	    exit 1;
       
  3010 	}
       
  3011     } else {
       
  3012 	# master mode has not been specified, add it.
       
  3013 	log_message "Enabling agentX mastering for SMA.\n";
       
  3014 	prepend_line({'meta'=>{'directive'=>'master', 'masterMode'=>'agentx'}});
       
  3015     }
       
  3016     @lines = get_lines('agentxTimeout', \%::SMA_CONFIGS); 
       
  3017     if (@lines) {
       
  3018 	if ($lines[0]->{'agentxTimeout'} < 2) {
       
  3019 	    $lines[0]->{'agentxTimeout'} = 2;
       
  3020 	    replace_line(\%::SMA_CONFIGS, $lines[0], $lines[0]);
       
  3021 	}
       
  3022     } else {
       
  3023 	prepend_line({'meta'=>{'directive'=>'agentxTimeout', 'agentxTimeout'=>2}});
       
  3024     }
       
  3025     @lines = get_lines('agentxRetries', \%::SMA_CONFIGS); 
       
  3026     if (@lines) {
       
  3027 	if ($lines[0]->{'agentxRetries'} < 4) {
       
  3028 	    $lines[0]->{'agentxRetries'} = 4;
       
  3029 	    replace_line(\%::SMA_CONFIGS, $lines[0], $lines[0]);
       
  3030 	}
       
  3031     } else {
       
  3032 	prepend_line({'meta'=>{'directive'=>'agentxRetries', 'agentxRetries'=>4}});
       
  3033     }
       
  3034 }
       
  3035 
       
  3036 sub check_agent_configs
       
  3037 {
       
  3038     my ($directive, $line);
       
  3039     for $directive ('agentgroup', 'agentuser', 'authtrapenable') {
       
  3040 	my (@masf) = get_lines($directive, \%::MASF_CONFIGS);
       
  3041 	my (@sma) = get_lines($directive, \%::SMA_CONFIGS);
       
  3042 	if (@masf > 0 && @sma == 0)  {
       
  3043 	    log_message "The following $directive directive was found in ".
       
  3044 	    "the MASF configuration but is not configured for SMA:\n";
       
  3045 	    log_message $masf[0]->{'new'}."\n";
       
  3046 
       
  3047 	} elsif (@masf > 0 && @sma > 0 && $sma[0] ne $masf[0]) {
       
  3048 	    log_message "The following $directive directive was found in ".
       
  3049 	    "the MASF configuration but differs in the  SMA configuration:\n";
       
  3050 	    log_message $masf[0]->{'new'}."\n";
       
  3051 	}
       
  3052 	for $line (@masf) {
       
  3053 	    # delete the line
       
  3054 	    replace_line(\%::MASF_CONFIGS, $line, {'meta'=>{}});
       
  3055 	}
       
  3056     }
       
  3057 }
       
  3058 
       
  3059 sub check_system_configs
       
  3060 {
       
  3061     my ($directive, $line);
       
  3062     for $directive ('syslocation', 'syscontact', 'sysname', 'sysservices') {
       
  3063 	my (@masf) = get_lines($directive, \%::MASF_CONFIGS);
       
  3064 	my (@sma) = get_lines($directive, \%::SMA_CONFIGS);
       
  3065 	if (@masf > 0 && @sma == 0)  {
       
  3066 	    log_message "The following $directive directive was found in ".
       
  3067 	    "the MASF configuration but is not configured for SMA:\n";
       
  3068 	    log_message $masf[0]->{'new'}."\n";
       
  3069 	    log_message "You may wish to set this parameter in the SMA ".
       
  3070 	    "configuration file after this script completes migration.\n";
       
  3071 
       
  3072 	} elsif (@masf > 0 && @sma > 0 && $sma[0]->{'new'} ne $masf[0]->{'new'}) {
       
  3073 	    log_message "The following $directive directive was found in ".
       
  3074 	    "the MASF configuration but differs in the  SMA configuration:\n";
       
  3075 	    log_message $masf[0]->{'new'}."\n";
       
  3076 	    log_message "You may wish to set this parameter in the SMA ".
       
  3077 	    "configuration file after this script completes migration.\n";
       
  3078 	}
       
  3079 	for $line (@masf) {
       
  3080 	    # delete the line
       
  3081 	    replace_line(\%::MASF_CONFIGS, $line, {'meta'=>{}});
       
  3082 	}
       
  3083     }
       
  3084 }
       
  3085 
       
  3086 sub process_agentaddress
       
  3087 {
       
  3088     my ($line, $lines, @masfAgentAddress, $smaAgentAddress, $config);
       
  3089     (@masfAgentAddress) = get_lines ('agentaddress', \%::MASF_CONFIGS);
       
  3090     if (@masfAgentAddress == 0) {
       
  3091 	log_message "No agentaddress directive found for MASF\n";
       
  3092 	return;
       
  3093     }
       
  3094 
       
  3095     # if more than one MASF agentaddress line was specified then all but the
       
  3096     # last are ignored so we should delete them
       
  3097     while (@masfAgentAddress > 1) {
       
  3098 	$line = shift @masfAgentAddress;
       
  3099 	replace_line (\%::MASF_CONFIGS, $line, {'meta'=>{}});
       
  3100     }
       
  3101 
       
  3102     my (@smaTokens, @masfTokens);
       
  3103     ($smaAgentAddress) = get_lines ('agentaddress', \%::SMA_CONFIGS);
       
  3104     if (! defined $smaAgentAddress) {
       
  3105 	log_message "SMA uses default 161 port\n";
       
  3106 	@smaTokens = ('udp:161');
       
  3107     } else {
       
  3108 	@smaTokens = parse_agentaddress ($smaAgentAddress->{'new'});
       
  3109     }
       
  3110 
       
  3111     if (defined $smaAgentAddress) {
       
  3112 	$line = $smaAgentAddress;
       
  3113 	$config = \%::SMA_CONFIGS;
       
  3114     } else {
       
  3115 	$line = $masfAgentAddress[0];
       
  3116 	$config = \%::MASF_CONFIGS;
       
  3117     }
       
  3118 
       
  3119     if ($::DISABLE_MASF_PORT && @masfAgentAddress) {
       
  3120 	log_message "Disabling access via old MASF port\n";
       
  3121 	delete $masfAgentAddress[0]->{'new'};
       
  3122 	return;
       
  3123     }
       
  3124 
       
  3125     @masfTokens = parse_agentaddress ($masfAgentAddress[0]->{'new'});
       
  3126     
       
  3127     # MASF addresses must be a subset of SMA
       
  3128     my ($i, $j, $okToMigrate);
       
  3129     $okToMigrate = 1;
       
  3130     for $i (@masfTokens) {
       
  3131 	if (grep(($i eq $_), @smaTokens) == 0) {
       
  3132 	    log_message "MASF port configuration for port $i conflicts with SMA\n";
       
  3133 	    $okToMigrate = 0;
       
  3134 	}
       
  3135     }
       
  3136     
       
  3137     if (! $okToMigrate && ! $::USE_MASF_PORT && ! $::DISABLE_MASF_PORT) {
       
  3138 	log_message "Unable to resolve conflict - aborting\n";
       
  3139 	exit 1;
       
  3140     }
       
  3141 
       
  3142     if ($::USE_MASF_PORT) {
       
  3143 	push @smaTokens, @masfTokens;
       
  3144     }
       
  3145     $line->{'new'} = 'agentaddress '.(join ',',@smaTokens);
       
  3146     replace_line($config, $line, $line);
       
  3147     return;
       
  3148 }
       
  3149 
       
  3150 ##############################################################################
       
  3151 # Main section starts here
       
  3152 
       
  3153 # configure Getopt for CLIP compliance
       
  3154 Getopt::Long::Configure('bundling', 'require_order',
       
  3155 	'no_getopt_compat', 'no_auto_abbrev', 'no_ignore_case');
       
  3156 
       
  3157 my ($selectCommunity, $selectUser, $useAgentPort, $trapFilter,
       
  3158 	$masterTrapTarget, $version, $help);
       
  3159 $selectCommunity = 'error';
       
  3160 $selectUser = 'error';
       
  3161 $useAgentPort = 'error';
       
  3162 $trapFilter = 'add';
       
  3163 
       
  3164 my $result = GetOptions(
       
  3165 	'i|ignore-unrecognized-directives'=>\$::IGNORE_UNRECOGNIZED_DIRECTIVES,
       
  3166 	's|skip-user'=>\$::IGNORE_ENGINEID,
       
  3167 	'y|select-community=s'=>\$selectCommunity,
       
  3168 	'u|select-user=s'=>\$selectUser,
       
  3169 	'p|use-agent-port=s'=>\$useAgentPort,
       
  3170 	't|trap-filter=s'=>\$trapFilter,
       
  3171 	'l|master-trap-target=s'=>\$masterTrapTarget,
       
  3172 	'c|no-community'=>\$::DONT_KEEP_V1V2C_USERS,
       
  3173 	'r|no-trap'=>\$::DONT_KEEP_TRAP_DESTS,
       
  3174 	'm|no-usmuser'=>\$::DONT_KEEP_V3_USERS,
       
  3175 	'n|dry-run'=>\$::DRY_RUN,
       
  3176 	'V|version'=>\$version,
       
  3177 	'help|?'=>\$help);
       
  3178 
       
  3179 if (! $result) {
       
  3180 	print STDERR "An unrecognized option was present.\n";
       
  3181 	help();
       
  3182 }
       
  3183 
       
  3184 if ($version) {
       
  3185 	version();
       
  3186 }
       
  3187 
       
  3188 # no interactive mode
       
  3189 $::AUTOMATED = 1;
       
  3190 
       
  3191 $::KEEP_MASF_GROUPS = 0;
       
  3192 $::KEEP_SMA_GROUPS = 0;
       
  3193 if ($selectCommunity eq 'agent') {
       
  3194     $::KEEP_MASF_GROUPS = 1;
       
  3195 } elsif ($selectCommunity eq 'master') {
       
  3196     $::KEEP_SMA_GROUPS = 1;
       
  3197 } elsif ($selectCommunity ne 'error') {
       
  3198     print STDERR "Invalid --select-community option $selectCommunity".
       
  3199 	" specified.\n";
       
  3200     help();
       
  3201 }
       
  3202 
       
  3203 $::KEEP_MASF_USM_USERS = 0;
       
  3204 $::KEEP_SMA_USM_USERS = 0;
       
  3205 if ($selectUser eq 'agent') {
       
  3206     $::KEEP_MASF_USM_USERS = 1;
       
  3207 } elsif ($selectUser eq 'master') {
       
  3208     $::KEEP_SMA_USM_USERS = 1;
       
  3209 } elsif ($selectUser ne 'error') {
       
  3210     print STDERR "Invalid --select-user option $selectUser".
       
  3211 	" specified.\n";
       
  3212     help();
       
  3213 }
       
  3214 
       
  3215 $::USE_MASF_PORT = 0;
       
  3216 $::DISABLE_MASF_PORT = 0;
       
  3217 if ($useAgentPort eq 'enable') {
       
  3218     $::USE_MASF_PORT = 1;
       
  3219 } elsif ($useAgentPort eq 'disable') {
       
  3220     $::DISABLE_MASF_PORT = 1;
       
  3221 } elsif ($useAgentPort ne 'error') {
       
  3222     print STDERR "Invalid --use-agent-port option $useAgentPort specified\n";
       
  3223     help();
       
  3224 }
       
  3225 
       
  3226 if ($trapFilter eq 'none') {
       
  3227     $::NO_TRAP_FILTERS = 1;
       
  3228 } elsif ($trapFilter eq 'add') {
       
  3229     $::NO_TRAP_FILTERS = 0;
       
  3230 } else {
       
  3231     print STDERR "Invalid --trap-filter option $trapFilter\n";
       
  3232     help();
       
  3233 }
       
  3234 	
       
  3235 $::EXTEND_SMA_FILTERS = 0;
       
  3236 $::KEEP_SNMP_TARGET_PARAMS = 0;
       
  3237 if (defined $masterTrapTarget) {
       
  3238     if ($trapFilter eq 'none') {
       
  3239 	print STDERR "--master-trap-target cannot be used with --trap-filter=none\n";
       
  3240 	help();
       
  3241     }
       
  3242     if ($masterTrapTarget eq 'agent') {
       
  3243 	$::EXTEND_SMA_FILTERS = 1;
       
  3244     } elsif ($masterTrapTarget eq 'master') {
       
  3245 	$::KEEP_SNMP_TARGET_PARAMS = 1;
       
  3246 	$::EXTEND_SMA_FILTERS = 1;
       
  3247     } else {
       
  3248 	print STDERR "Invalid --master-trap-target option $masterTrapTarget specified.\n";
       
  3249 	help();
       
  3250     }
       
  3251 }
       
  3252 
       
  3253 if ($help) {
       
  3254     help();
       
  3255 }
       
  3256 
       
  3257 # set the umask before writing to the log file
       
  3258 set_umask();
       
  3259 log_message "masfcnv ".localtime()."\n\n";
       
  3260 are_we_root();
       
  3261 stop_sma_running();
       
  3262 stop_masf_running();
       
  3263 
       
  3264 @::MASF_CONFIG_FILES = ("/etc/opt/SUNWmasf/conf/snmpd.conf");
       
  3265 $::MASF_PERSISTENT_FILE = "/var/opt/SUNWmasf/snmpd.dat";
       
  3266 $::MASF_PERSISTENT_DIR = "/var/opt/SUNWmasf";
       
  3267 
       
  3268 @::SMA_CONFIG_FILES = ("/usr/lib/net-snmp/snmpd.conf");
       
  3269 $::SMA_PERSISTENT_FILE = "/var/net-snmp/snmpd.conf";
       
  3270 $::SMA_PERSISTENT_DIR = "/var/net-snmp";
       
  3271 sma_config_sanity_check();
       
  3272 masf_config_sanity_check();
       
  3273 read_config_files();
       
  3274 sanity_check_config_files();
       
  3275 add_metadata();
       
  3276 install_agentx_config();
       
  3277 process_agentaddress();
       
  3278 convert_rwcommunities();
       
  3279 expand_rocommunities(\%::MASF_CONFIGS, [$ENTITY_MIB_OID, $SUNPLAT_MIB_OID]);
       
  3280 expand_rocommunities(\%::SMA_CONFIGS, [$INTERNET_OID]);
       
  3281 convert_rwusers();
       
  3282 expand_rousers(\%::MASF_CONFIGS, [$ENTITY_MIB_OID, $SUNPLAT_MIB_OID]);
       
  3283 expand_rousers(\%::SMA_CONFIGS, [$INTERNET_OID]);
       
  3284 clean_group_membership();
       
  3285 if (! $::DONT_KEEP_V1V2C_USERS) {
       
  3286     uniquify_securityNames('masf');
       
  3287     process_com2sec();
       
  3288 } else {
       
  3289     remove_v1v2c_users();
       
  3290 }
       
  3291 uniquify_groupNames('masf');
       
  3292 check_usm_securityNames();
       
  3293 process_engineIDs();
       
  3294 if (! $::DONT_KEEP_V3_USERS) {
       
  3295     process_usm_securityNames();
       
  3296 } else {
       
  3297     remove_v3_users();
       
  3298 }
       
  3299 uniquify_viewNames('masf');
       
  3300 
       
  3301 process_trapcommunity(\%::MASF_CONFIGS);
       
  3302 process_trapcommunity(\%::SMA_CONFIGS);
       
  3303 if ($::DONT_KEEP_TRAP_DESTS) {
       
  3304     remove_trap_destinations();
       
  3305 }
       
  3306 check_duplicate_trap_destinations();
       
  3307 if ($::NO_TRAP_FILTERS) {
       
  3308     # simplistic trap processing
       
  3309     # no further filtering
       
  3310 } else {
       
  3311 
       
  3312     if ($::EXTEND_SMA_FILTERS) {
       
  3313 	# "master"
       
  3314 	# if the administrator selects EXTEND_SMA_FILTERS and
       
  3315 	# KEEP_SNMP_TARGET_PARAMS is selected then: MASF trap destinations are
       
  3316 	# migrated using existing SMA targetParams, targetAddrs, filterProfiles
       
  3317 	# if possible. SMA trap destinations keep existing targetParams and
       
  3318 	# have filterProfiles updated to INCLUDE MASF traps. Targets present in
       
  3319 	# both SMA and MASF configs end up receiving all traps with SMA params.
       
  3320 	# 1. Update SMA filterProfiles to include MASF traps
       
  3321 	# 2. Expand existing MASF trapsinks to targetAddrs, params, and profiles
       
  3322 	# identifying overlapping trap destinations and using them where
       
  3323 	# appropriate
       
  3324 	
       
  3325 	# "agent"
       
  3326 	# if the administrator selects EXTEND_SMA_FILTERS and
       
  3327 	# KEEP_SNMP_TARGET_PARAMS is not selected then: MASF trap destinations
       
  3328 	# are migrated using MASF targetParams, targetAddrs, filterProfiles.
       
  3329 	# All SMA trap destinations not in MASF use SMA params and have MASF
       
  3330 	# traps INCLUDED.  New params are created for targets present in both
       
  3331 	# SMA and MASF which use tag specifically for MASF. Targets present in
       
  3332 	# both SMA and MASF configs end up receiving all traps with MASF params.
       
  3333 	# 1. Update SMA filterProfiles to include MASF traps 
       
  3334 	# 2. Expand existing MASF trapsinks to new targetAddrs, params,
       
  3335 	# profiles, identifying overlapping trap destinations and profiles,
       
  3336 	# transferring them to new SMA params and retain original profiles.
       
  3337 	
       
  3338 	# selecting KEEP_SNMP_TARGET_PARAMS without EXTEND_SMA_FILTERS is not an
       
  3339 	# option
       
  3340 	extend_sma_trap_filters();
       
  3341     }
       
  3342     # "add"
       
  3343     # if the administrator selects neither option then: All MASF trap
       
  3344     # configurations are translated to new params, targetAddrs, filters which
       
  3345     # have ONLY MASF traps included in the profile.  Targets present in both
       
  3346     # MASF and SMA may receive duplicate traps, depending upon the SMA filter
       
  3347     # profile.
       
  3348     # 1. Expand existing MASF trapsinks to new targetAddrs, params, profiles.
       
  3349     process_trapsinks();
       
  3350 }
       
  3351 check_system_configs();
       
  3352 check_agent_configs();
       
  3353 convert_metadata();
       
  3354 if (! $::DRY_RUN) {
       
  3355     backup_files();
       
  3356     remove_masf_persistent_file();
       
  3357 }
       
  3358 if ($::DRY_RUN) {
       
  3359     print "Contents of ".$::SMA_CONFIG_FILES[0]."\n";
       
  3360     print "\n";
       
  3361     *FH = *STDOUT;
       
  3362 } else {
       
  3363     open (FH, "> ".$::SMA_CONFIG_FILES[0]) || die "Couldn't open file ".$::SMA_CONFIG_FILES[0]." for writing.\n";
       
  3364 }
       
  3365 my ($l);
       
  3366 for $l (@{$::ADDED_CONFIGS{'prepend'}}) {
       
  3367     print_line (\*FH, $l);
       
  3368 }
       
  3369 dump_config(\*FH, $::SMA_PERSISTENT_FILE, \%::SMA_CONFIGS);
       
  3370 dump_config(\*FH, $::MASF_PERSISTENT_FILE, \%::MASF_CONFIGS);
       
  3371 for $l (@{$::ADDED_CONFIGS{'append'}}) {
       
  3372     print_line (\*FH, $l);
       
  3373 }
       
  3374 if ($::DRY_RUN) {
       
  3375     print "=======================\n";
       
  3376     print "Contents of ".$::SMA_PERSISTENT_FILE."\n";
       
  3377     print "=======================\n";
       
  3378 } else {
       
  3379     close FH;
       
  3380     open (FH, "> ".$::SMA_PERSISTENT_FILE) || die "Couldn't open file ".$::SMA_PERSISTENT_FILE." for writing.\n";
       
  3381 }
       
  3382 dump_persistent_storage(\*FH, $::SMA_PERSISTENT_FILE, \%::SMA_CONFIGS);
       
  3383 dump_persistent_storage(\*FH, $::MASF_PERSISTENT_FILE, \%::MASF_CONFIGS);
       
  3384 if (! $::DRY_RUN) {
       
  3385     close FH;
       
  3386     install_template_config_file();
       
  3387     install_new_wrapper_script();
       
  3388 }