1 #
   2 # CDDL HEADER START
   3 #
   4 # The contents of this file are subject to the terms of the
   5 # Common Development and Distribution License (the "License").
   6 # You may not use this file except in compliance with the License.
   7 #
   8 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9 # or http://www.opensolaris.org/os/licensing.
  10 # See the License for the specific language governing permissions
  11 # and limitations under the License.
  12 #
  13 # When distributing Covered Code, include this CDDL HEADER in each
  14 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15 # If applicable, add the following below this CDDL HEADER, with the
  16 # fields enclosed by brackets "[]" replaced with your own identifying
  17 # information: Portions Copyright [yyyy] [name of copyright owner]
  18 #
  19 # CDDL HEADER END
  20 #
  21 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  22 # Use is subject to license terms.
  23 #
  24 # Slim Install Transfer Module
  25 #
  26 import os
  27 import errno
  28 import time
  29 import sys
  30 import shutil
  31 import traceback
  32 import fcntl
  33 import array
  34 import string
  35 import threading
  36 import logging
  37 import operator
  38 import tempfile
  39 from stat import *
  40 from subprocess import *
  41 from osol_install.install_utils import *
  42 
  43 #
  44 # Modules specific to Slim Install
  45 #
  46 import liblogsvc as logsvc
  47 import libtransfer as tmod
  48 
  49 class TM_defs(object):
  50         """Class that holds some globally used values
  51         """
  52         MAX_NUMFILES = 200000.0
  53         FIND_PERCENT = 4
  54         KBD_DEVICE = "/dev/kbd"
  55         CPIO = "/usr/bin/cpio"
  56         PKG = "/usr/bin/pkg"
  57         KBD_LAYOUT_FILE = "/usr/share/lib/keytables/type_6/kbd_layouts"
  58         KBD_DEFAULTS_FILE = "etc/default/kbd"
  59         MOUNT = "/usr/sbin/mount -o ro,nologging "
  60         GZCAT = "/usr/bin/gzcat "
  61         GZCAT_DST = "/var/run/boot_archive"
  62         
  63         def __init__(self):
  64                 self.tm_lock = None
  65                 self.do_abort = 0
  66                 self.percent = 0.0
  67 
  68 class Cpio_spec(object):
  69         """Class used to hold values specifying a mountpoint for cpio operation
  70         """
  71         def __init__(self, chdir_prefix=None, cpio_dir=None,
  72             match_pattern=None,  clobber_files=0, cpio_args="pdum",
  73             file_list=None):
  74                 self.chdir_prefix = chdir_prefix
  75                 self.cpio_dir = cpio_dir
  76                 self.match_pattern = match_pattern
  77                 self.clobber_files = clobber_files
  78                 self.cpio_args = cpio_args
  79                 self.file_list = file_list
  80 
  81 class Flist(object):
  82         """Class used to hold file list entries for cpio operation
  83         """
  84         def __init__(self, name=None, chdir_prefix=None, clobber_files=0,
  85             cpio_args=None):
  86                 self.name = name
  87                 self.chdir_prefix = chdir_prefix
  88                 self.clobber_files = clobber_files
  89                 self.cpio_args = cpio_args
  90 
  91         def open(self):
  92                 self.handle = open(self.name, "w+")
  93 
  94 class TError(Exception):
  95         """Base class for Transfer Module Exceptions.
  96         """
  97         pass
  98 
  99 class TValueError(TError):
 100         """Exception raised for errors in the input key/value list.
 101 
 102         Attributes:
 103            message -- explanation of the error
 104            retcode -- error return code
 105         """
 106 
 107         def __init__(self, message, retcode):
 108                 self.message = message
 109                 self.retcode = retcode
 110 
 111 class TAbort(TError):
 112         """Exception raised when the caller requests to abort transfer
 113         operation midway or a failure is detected.
 114         
 115         Attributes:
 116            message -- explanation of the abort operation
 117            retcode -- error return code
 118         """
 119         
 120         def __init__(self, message, retcode = errno.EINTR):
 121                 self.message = message
 122                 self.retcode = retcode
 123 
 124 class TIPSPkgmissing :
 125         """Exception raised if an IPS package is missing
 126         Attribute: retcode -- error return code
 127         """
 128 
 129         def __init__(self, retcode = errno.ENOENT):
 130                 self.retcode = retcode
 131 
 132 def tm_abort_transfer() :
 133         """"Method to signal to abort the transfer
 134         """
 135         if params.tm_lock.locked():
 136                 params.tm_lock.release()        
 137         else: 
 138                 params.do_abort = 1;
 139 
 140 def tm_abort_signaled():
 141         """Method to detect abort
 142         """
 143         return params.do_abort
 144 
 145 class ProgressMon(object):
 146 
 147         def startmonitor(self, fs, distrosize, message, initpct=0, endpct=100):
 148                 """Start thread to monitor progress in populating file system
 149                         fs - file system to monitor
 150                         distrosize = full distro size in kilobytes
 151                         message = progress message to log. 
 152                         initpct = base percent value from which to start
 153                             calculating.
 154                         endpct = percentage value at which to stop calculating
 155                 """
 156                 self.message =  message
 157                 self.distrosize = distrosize
 158                 self.initpct = initpct
 159                 self.endpct = endpct
 160                 self.done = False
 161                 self.thread1 = threading.Thread( target=self.__progressthread,
 162                     args=( fs,))
 163                 self.thread1.start()
 164                 return 0
 165 
 166         def wait(self):
 167                 self.thread1.join()
 168 
 169         def __progressthread(self, fs):
 170                 """Monitor progress in populating file system
 171                         fs - file system to monitor
 172                 """
 173                 initsize = self.__fssize(fs)
 174                 totpct = self.endpct - self.initpct
 175                 prevpct = -1 
 176 
 177                 # Loop until the user aborts or we're done transferring.
 178                 # Keep track of the percentage done and let the user know
 179                 # how far the transfer has progressed.
 180                 while 1:
 181                         # Compute increase in fs size
 182                         fssz = self.__fssize(fs)
 183                         if (fssz == -1):
 184                                 return -1
 185                         fsgain = fssz - initsize
 186 
 187                         # Compute percentage transfer
 188                         actualpct = fsgain * 100 / self.distrosize
 189                         # Compute percentage transfer in terms of stated range
 190                         pct = fsgain * totpct / self.distrosize + self.initpct
 191                         # Do not exceed limits
 192                         if pct >= self.endpct or actualpct > 100:
 193                                 pct = self.endpct       
 194                         # If the percentage has changed at all, log the
 195                         # progress so the user can see something is going on.
 196                         if (pct != prevpct):
 197                                 tmod.logprogress(int(pct), self.message)
 198                                 prevpct = pct
 199                         if pct >= self.endpct:
 200                                 return 0
 201                         time.sleep(2)
 202                         if tm_abort_signaled() or self.done:
 203                                 return 0
 204 
 205         def __fssize(self, fs):
 206                 """Find the current size of the specified file system.
 207                         fs - file system to monitor
 208                 Returns the size of the filesystem in kilobytes
 209                 """     
 210                 cmd = "/usr/bin/df -k " + fs
 211                 p  = os.popen(cmd)
 212                 # Read the header and throw it away....
 213                 dfout = p.readline()
 214                 if not dfout:
 215                         logsvc.write_log(TRANSFER_ID, "FS size computation " +
 216                             "failed for " + fs + "\n")
 217                         return -1
 218                 # read the line with the size information.
 219                 dfout = p.readline()
 220                 if not dfout:
 221                         logsvc.write_log(TRANSFER_ID, "FS size computation " +
 222                             "failed for " + fs + "\n")
 223                         return -1
 224                 # and yank out the size information
 225                 line_tokens = dfout.split()
 226                 return int(line_tokens[2])
 227 
 228 
 229 class Transfer_cpio(object):
 230         """This class contains all the methods used to actually transfer
 231         files from the src_mntpt or / to the dst_mntpt
 232         """
 233 
 234         def __init__(self):
 235                 self.dst_mntpt = ""
 236                 self.src_mntpt = ""
 237                 self.cpio_action = "" 
 238                 self.cpio_args = "pdum"
 239                 self.list_file = ""
 240                 self.skip_file_list = ""
 241                 self.tformat = "%a, %d %b %Y %H:%M:%S +0000"
 242                 self.debugflag = 0 
 243                 self.cpio_prefixes = []
 244                 self.image_info = ""
 245                 self.distro_size = 0
 246                 self.log_handler = None
 247                 self.unpack_archive = None
 248 
 249                 # TODO: This is live media specific and shouldn't be part
 250                 # of transfer mod.
 251                 # List of toplevel directories that should be transferred
 252                 self.cpio_prefixes.append(Cpio_spec(chdir_prefix="/", \
 253                     cpio_dir="."))
 254                 self.cpio_prefixes.append(Cpio_spec(chdir_prefix="/", \
 255                     cpio_dir="usr"))
 256                 self.cpio_prefixes.append(Cpio_spec(chdir_prefix="/", \
 257                     cpio_dir="opt"))
 258                 self.cpio_prefixes.append(Cpio_spec(chdir_prefix="/", \
 259                     cpio_dir="dev"))
 260                 self.cpio_prefixes.append(Cpio_spec(chdir_prefix="/mnt/misc", \
 261                     cpio_dir=".", clobber_files=1, cpio_args="pdm"))
 262                 # When a file_list is provided, the cpio operation
 263                 # will be done to the list of files provided in that file.
 264                 # There will not be a os.walk() of the "chdir_prefix".
 265                 # The "chdir_prefix" value is still important here, because
 266                 # the content list is generated assuming "chdir_prefix"
 267                 # is the root.
 268                 self.cpio_prefixes.append(Cpio_spec(chdir_prefix="/.cdrom", \
 269                     cpio_dir=".", file_list="/.cdrom/.livecd-cdrom-content"))
 270                         
 271         
 272         def info_msg(self, msg):
 273                 """Log an informational message to logging service
 274                 """
 275                 if (self.log_handler != None):
 276                         self.log_handler.info(msg)
 277                 else:
 278                         logsvc.write_log(TRANSFER_ID, msg + "\n")
 279 
 280         def prerror(self, msg):
 281                 """Log an error message to logging service and stderr
 282                 """
 283                 if (self.log_handler != None):
 284                         self.log_handler.error(msg)
 285                 else:
 286                         msg1 = msg + "\n"
 287                         logsvc.write_dbg(TRANSFER_ID, logsvc.LS_DBGLVL_ERR,
 288                             msg1)
 289                         sys.stderr.write(msg1)
 290                         sys.stderr.flush()
 291 
 292         def dbg_msg(self, msg):
 293                 """Log detailed debugging messages to logging service
 294                 """
 295                 if (self.log_handler != None):
 296                         self.log_handler.debug(msg)
 297                 else:
 298                         if (self.debugflag > 0):
 299                                 logsvc.write_dbg(TRANSFER_ID,
 300                                     logsvc.LS_DBGLVL_INFO, msg + "\n")
 301 
 302         # TODO : This shouldn't be part of transfer_mod
 303         def do_clobber_files(self, flist_file):
 304                 """Given a file containing a list of pathnames this function
 305                 will search for those entries in the alternate root and
 306                 delete all matching pathnames from the alternate root that
 307                 are symbolic links.
 308                 This process is required because of the way the LiveCD env
 309                 is constructed. Some of the entries in the boot_archive are
 310                 symbolic links to files mounted off a compressed lofi file.
 311                 This is done to drastically reduce space usage by the
 312                 boot_archive.
 313                 """
 314                 self.dbg_msg("File list for clobber: " + flist_file)
 315                 fh = open(flist_file, 'r')
 316                 os.chdir(self.dst_mntpt)
 317                 for line in fh:
 318                         line = line[:-1]
 319 
 320                         try:
 321                                 mst = os.lstat(line)
 322                                 if S_ISLNK(mst.st_mode):
 323                                         self.dbg_msg("Unlink: " + line)
 324                                         os.unlink(line)
 325                         except OSError:
 326                                 pass
 327                 fh.close()
 328 
 329         # TODO: This shouldn't be part of transfer_mod
 330         def get_kbd_layout_name(self, lnum):
 331                 """Given a keyboard layout number return the layout string.
 332                 We should not be doing this here, but unfortunately there
 333                 is no interface in the OpenSolaris keyboard API to perform
 334                 this mapping for us - RFE.
 335                 """
 336                 fh = open(TM_defs.KBD_LAYOUT_FILE, 'r')
 337 
 338                 name = ""
 339                 for line in fh:
 340                         if line[0] == '#':
 341                                 continue
 342 
 343                         if line.find('=') == -1:
 344                                 continue
 345 
 346                         (name, num) = line.split('=')
 347                         if int(num) == lnum:
 348                                 break
 349                 fh.close()
 350                 return name
 351 
 352         def check_abort(self):
 353                 if tm_abort_signaled() == 1:
 354                         raise TAbort("User aborted transfer")
 355 
 356         def build_cpio_entire_file_list(self):
 357                 """Do a file tree walk of all the mountpoints provided and
 358                 build up pathname lists. Pathname lists of all mountpoints
 359                 under the same prefix are aggregated in the same file to
 360                 reduce the number of cpio invocations.
 361                 """     
 362                 
 363                 self.info_msg("-- Starting transfer process, " +
 364                     time.strftime(self.tformat) + " --")
 365                 self.check_abort()
 366 
 367                 tmod.logprogress(0, "Building file lists for cpio")
 368 
 369                 if self.src_mntpt != "" and self.src_mntpt != "/":
 370                         self.cpio_prefixes = []
 371                         self.cpio_prefixes.append(Cpio_spec(
 372                             chdir_prefix=self.src_mntpt, cpio_dir=".",
 373                             cpio_args=self.cpio_args))
 374 
 375                 total_find_percent = (len(self.cpio_prefixes) - 1) * \
 376                     TM_defs.FIND_PERCENT
 377 
 378                 # Get the optimized libc overlay out of the way.
 379                 # Errors from umount intentionally ignored
 380                 os.system("umount /lib/libc.so.1")
 381 
 382                 self.info_msg("Building cpio file lists")
 383 
 384                 # set up some variables with startup values.
 385                 opercent = 0.0
 386 
 387                 # Original values to compare against to see if
 388                 # we need to generate a different list of files
 389                 # to cpio.
 390                 old_cprefix = ""
 391                 patt = None
 392                 i = 0
 393                 nfiles = 0.0
 394                 # file entry list. List of files containing the files
 395                 # to cpio.
 396                 fent_list = []
 397                 fent = None
 398                 self.check_abort()
 399 
 400                 #
 401                 # Do a file tree walk of all the mountpoints provided and
 402                 # build up pathname lists. Pathname lists of all mountpoints
 403                 # under the same prefix are aggregated in the same file to
 404                 # reduce the number of cpio invocations.
 405                 #
 406                 # This loop builds a list where each entry points to a file
 407                 # containing a pathname list and mentions other info like the
 408                 # mountpoint from which to copy etc.
 409                 #
 410                 for cp in self.cpio_prefixes:
 411                         self.dbg_msg("Cpio dir: " + cp.cpio_dir +
 412                             " Chdir to: " + cp.chdir_prefix)
 413                         patt = cp.match_pattern
 414                         self.check_abort()
 415 
 416                         if patt != None and patt[0] == '!':
 417                                 negate = 1
 418                                 patt = patt[1:]
 419                         else:
 420                                 negate = 0
 421 
 422                         # Check to be sure the specified cpio source
 423                         # directory is accessable.
 424                         st = None
 425                         try:
 426                                 os.chdir(cp.chdir_prefix)
 427                                 st = os.stat(cp.cpio_dir)
 428                         except OSError:
 429                                 raise TAbort("Failed to access Cpio dir: " +
 430                                     traceback.format_exc(),
 431                                     TM_E_CPIO_ENTIRE_FAILED)
 432 
 433                         # Create a new file if the prefix, or cpio_args 
 434                         # or clobber files, have changed, or a
 435                         # file containing a pre-generated list of
 436                         # content is provided.
 437                         if (old_cprefix != cp.chdir_prefix or
 438                             patt != None or
 439                             cp.clobber_files == 1 or cp.cpio_args != None or
 440                             cp.file_list != None):
 441                                 # create a temporary file that will
 442                                 # contain the list of files to cpio
 443                                 # temp files are /var/run/flist<number>
 444                                 fent = Flist()
 445                                 fent_list.append(fent)
 446                                 old_cprefix = cp.chdir_prefix
 447                                 fent.name = "/var/run/flist" + str(i)
 448                                 fent.open()
 449                                 i = i + 1
 450                                 self.dbg_msg(" File list tempfile:" +
 451                                     fent.name)
 452                                 fent.handle = open(fent.name, "w+")
 453                                 fent.chdir_prefix = cp.chdir_prefix
 454                                 fent.clobber_files = cp.clobber_files
 455                                 if (cp.cpio_args):
 456                                         fent.cpio_args = cp.cpio_args
 457 
 458                         # If the matching pattern is negated (!)
 459                         # flag this and remove the ! from the pattern
 460                         # before compiling it.
 461                         if (patt != None):
 462                                 cpatt = re.compile(patt)
 463 
 464                         self.info_msg("Scanning " + cp.chdir_prefix + "/" +
 465                             cp.cpio_dir)
 466 
 467                         # This is for temporarily storing the list of inode
 468                         # numbers and their corresponding file names.  This
 469                         # list will later be sorted by the inode number before
 470                         # it is written out to the cpio file list.
 471                         tmp_flist=[]
 472 
 473                         if (cp.file_list):
 474                                 try:
 475                                         image_content = open(cp.file_list, 'r')
 476                                 except IOError:
 477                                         raise TAbort("Failed to access " +
 478                                             cp.file_list,
 479                                             TM_E_INVALID_CPIO_FILELIST_ATTR)
 480 
 481                                 for fname in image_content:
 482 
 483                                         # Remove the '\n' character from
 484                                         # each of the lines read from the file
 485                                         if (fname[-1:] == '\n'):
 486                                                 fname = fname[:-1]
 487                                         try:
 488                                                 st1 = os.lstat(fname)
 489                                         except Exception, ex:
 490                                                 self.info_msg("Warning: Error" +
 491                                                     " processing " + fname +
 492                                                     "from file " + cp.file_list)
 493                                                 continue
 494 
 495                                         # Store the extent location of the
 496                                         # hsfs file and the filename to a 
 497                                         # temporary list
 498                                         tmp_flist.append((st1.st_ino, fname))
 499                         else:
 500                                 #
 501                                 # os.walk does not recurse into directory
 502                                 # symlinks so nftw(..., FTW_PHYS) is satisfied.
 503                                 # In addition, we want to restrict our search
 504                                 # only to the current filesystem which is
 505                                 # handled below.
 506                                 #
 507                                 for root, dirs, files in os.walk(cp.cpio_dir):
 508                                         self.check_abort()
 509                                         for name in files:
 510                                                 match = None
 511                                                 fname = root + "/" + name
 512                                                 if patt != None:
 513                                                         match = \
 514                                                             cpatt.match(name)
 515                                                         # If we have a match on
 516                                                         # the name but the
 517                                                         # pattern was !, then
 518                                                         # that's really a
 519                                                         # non-match.  Also, if
 520                                                         # no match is found
 521                                                         # but the pattern
 522                                                         # was not ! it's a
 523                                                         # non-match.
 524                                                         if (match != None \
 525                                                             and negate == 1) \
 526                                                             or (match == None \
 527                                                             and negate != 1):
 528                                                                 self.dbg_msg(
 529                                                                     "Non " \
 530                                                                     "match. " \
 531                                                                     "Skipped:" \
 532                                                                     + fname)
 533                                                                 continue
 534 
 535                                                 try:
 536                                                         st1 = os.lstat(fname)
 537                                                 except:
 538                                                         self.info_msg(
 539                                                             "Warning: Error" +
 540                                                             " processing " +
 541                                                             fname)
 542                                                         continue
 543 
 544                                                 # Store the extent location of 
 545                                                 # the hsfs file and the
 546                                                 # filename to a temporary list
 547                                                 tmp_flist.append((st1.st_ino,
 548                                                     fname))
 549 
 550                                                 nfiles = nfiles + 1
 551                                                 params.percent = int(nfiles /
 552                                                     TM_defs.MAX_NUMFILES *
 553                                                     total_find_percent)
 554                                                 if params.percent - opercent \
 555                                                     > 1:
 556                                                         tmod.logprogress(
 557                                                             params.percent,
 558                                                             "Building cpio " \
 559                                                             "file lists")
 560                                                         opercent = \
 561                                                             params.percent
 562 
 563                                         #
 564                                         # Identify directories that we do not
 565                                         # want to traverse.  These are those
 566                                         # that can't be read for some reason
 567                                         # or those holding other mounted
 568                                         # filesystems.
 569                                         #
 570                                         rmlist = []
 571                                         for name in dirs:
 572                                                 dname = root + "/" + name
 573                                                 try:
 574                                                         st1 = os.stat(dname);
 575                                                 except:
 576                                                         rmlist.append(name)
 577                                                         continue
 578 
 579                                                 # Store the extent location of 
 580                                                 # the hsfs file and the
 581                                                 # filename to a temporary list
 582                                                 tmp_flist.append((st1.st_ino,
 583                                                     dname))
 584 
 585                                                 # Emulate nftw(..., FTW_MOUNT)
 586                                                 # for directories.
 587                                                 if st1.st_dev != st.st_dev:
 588                                                         rmlist.append(name)
 589 
 590                                         #
 591                                         # Remove directories so that they are
 592                                         # not traversed.   os.walk allows dirs
 593                                         # to be modified in place.
 594                                         #
 595                                         for dname in rmlist:
 596                                                 dirs.remove(dname)
 597 
 598                         # Write file list out to the file, after sorting
 599                         # by the inode number, which is the first item
 600                         tmp_flist.sort(key=operator.itemgetter(0))
 601                         lf = fent.handle
 602                         for f in map(operator.itemgetter(1), tmp_flist):
 603                                 lf.write(f + "\n")
 604                         lf.flush()
 605                                 
 606                 for fent in fent_list:
 607                         fent.handle.close()
 608                         fent.handle = None
 609                 return fent_list
 610 
 611         def cpio_skip_files(self):
 612                 """Function to remove the files listed in the skip file list.
 613                 Copying and then deleting the files is equivalent to not copying
 614                 them at all or "skipping" them.
 615                 """
 616                 try:
 617                         skip_file = open(self.skip_file_list, 'r')
 618                 except IOError:
 619                         raise TAbort("Failed to access " +
 620                             self.skip_file_list, TM_E_INVALID_CPIO_ACT_ATTR)
 621 
 622                 for line in skip_file:
 623                         os.unlink(self.dst_mntpt + "/" + line.rstrip())
 624                         
 625                 skip_file.close()
 626 
 627         def run_command(self, cmd):
 628                 try:
 629                         rt = call(cmd, shell=True)
 630                         if rt < 0:
 631                                 raise TAbort("Command " + cmd + 
 632                                     " terminated with signal", -rt)
 633                         elif rt > 0:
 634                                 raise TAbort("Command " + cmd + " failed", rt)
 635                 except OSError, e:
 636                         raise TAbort("Execution of " + cmd + " failed", e)
 637                 
 638         def mount_archive(self, mntdir):
 639                 self.run_command(TM_defs.GZCAT + self.unpack_archive + " > " +
 640                     TM_defs.GZCAT_DST)
 641                 self.run_command(TM_defs.MOUNT + TM_defs.GZCAT_DST + " " +
 642                     mntdir)
 643 
 644         def cpio_transfer_entire_directory(self):
 645                 # If an unpack archive was specified, mount it first and
 646                 # prepend it to the list of prefixes in order to ensure its
 647                 # contents can be overlaid by contents from the running instance
 648                 if (self.unpack_archive != None):
 649                         mntdir = tempfile.mkdtemp(dir="/var/run")
 650                         self.mount_archive(mntdir)
 651                         self.cpio_prefixes.insert(0,
 652                             Cpio_spec(chdir_prefix=mntdir, cpio_dir="."))
 653                 
 654                 fent_list = self.build_cpio_entire_file_list()
 655                 self.cpio_transfer_filelist(fent_list, TM_E_CPIO_ENTIRE_FAILED)
 656                 for fent in fent_list:
 657                         os.unlink(fent.name)
 658                         fent.name = ""
 659 
 660                 if self.skip_file_list:
 661                         self.cpio_skip_files()
 662 
 663         def cpio_transfer_filelist(self, fent_list, err_code):
 664                 self.info_msg("Beginning cpio actions")
 665 
 666                 #
 667                 # Now process each entry in the list. cpio is executed with the
 668                 # -V option so that it prints a dot for each pathname processed.
 669                 # This is needed to provide the ability to abort midway.
 670                 #
 671 
 672                 #
 673                 # Start the disk space monitor thread
 674                 #
 675                 if self.distro_size:
 676                         pmon = ProgressMon()
 677                         pmon.startmonitor(self.dst_mntpt, self.distro_size,
 678                             "Transferring Contents", params.percent, 95)
 679 
 680                 # Walk file lists, cpio'ing each in turn.       
 681                 for fent in fent_list:
 682                         self.check_abort()
 683 
 684                         if fent.clobber_files == 1:
 685                                 self.do_clobber_files(fent.name)
 686 
 687                         try:
 688                                 os.chdir(fent.chdir_prefix)
 689                         except OSError:
 690                                 raise TAbort("Failed to access " +
 691                                     fent.chdir_prefix, err_code)
 692                         cmd = TM_defs.CPIO + " -" + fent.cpio_args + "V " + \
 693                             self.dst_mntpt + " < " + fent.name
 694                         self.dbg_msg("Executing: " + cmd + " CWD: " +
 695                             fent.chdir_prefix)
 696                         err_file = os.tmpfile()
 697                         if (self.log_handler != None): 
 698                                 rt = exec_cmd_outputs_to_log(cmd.split(),
 699                                     self.log_handler)
 700                                 if (rt != 0):
 701                                         self.log_handler.error(cmd +
 702                                             " had errors")
 703                         else:
 704                                 pipe = Popen(cmd, shell=True, stdout=PIPE,
 705                                     stderr=err_file, close_fds=True)
 706                                 while 1:
 707                                         ch = pipe.stdout.read(1)
 708                                         self.check_abort()
 709                                         if not ch:
 710                                                 break;
 711                                 rt = pipe.wait()
 712 
 713                                 if rt != 0 and self.debugflag == 1:
 714                                         err_file.seek(0)
 715                                         self.info_msg("WARNING: " + cmd
 716                                             + " had errors")
 717                                         self.info_msg("         "
 718                                             + err_file.read())
 719 
 720                                 err_file.close()
 721 
 722                 if self.distro_size:
 723                         pmon.done = True
 724                         pmon.wait()
 725 
 726 
 727         def perform_transfer(self, args):
 728                 """Main function for doing the copying of bits
 729                 """
 730                 for opt, val in args:
 731                         if opt == TM_ATTR_MECHANISM:
 732                                 continue
 733                         elif opt == "dbgflag":
 734                                 if val == "true":
 735                                         self.debugflag = 1
 736                                 else:
 737                                         self.debugflag = 0
 738 
 739                         elif opt == TM_CPIO_DST_MNTPT:
 740                                 self.dst_mntpt = val
 741                         elif opt == TM_CPIO_SRC_MNTPT:
 742                                 self.src_mntpt = val
 743                         elif opt == TM_CPIO_ACTION:
 744                                 self.cpio_action = val
 745                         elif opt == TM_CPIO_LIST_FILE:
 746                                 self.list_file = val
 747                         elif opt == TM_ATTR_IMAGE_INFO:
 748                                 self.image_info = val
 749                         elif opt == TM_CPIO_ENTIRE_SKIP_FILE_LIST:
 750                                 self.skip_file_list = val
 751                         elif opt == TM_CPIO_ARGS:
 752                                 self.cpio_args = val
 753                         elif opt == TM_PYTHON_LOG_HANDLER:
 754                                 self.log_handler = val
 755                         elif opt == TM_UNPACK_ARCHIVE:
 756                                 self.unpack_archive = val
 757                         else:
 758                                 raise TValueError("Invalid attribute " +
 759                                     str(opt), TM_E_INVALID_TRANSFER_TYPE_ATTR)
 760 
 761                 if self.cpio_action == TM_CPIO_LIST and \
 762                     self.list_file == "":
 763                         raise TValueError("No list file for List Cpio action",
 764                             TM_E_INVALID_CPIO_FILELIST_ATTR)
 765 
 766                 if self.dst_mntpt == "":
 767                         raise TValueError("Target mountpoint not set",
 768                             TM_E_INVALID_CPIO_ACT_ATTR)
 769 
 770                 if self.cpio_action == TM_CPIO_ENTIRE and \
 771                     self.image_info == "":
 772                         self.image_info = "/.cdrom/.image_info"
 773 
 774                 # Check that the dst_mntpt really exists. If not, error.
 775                 try:
 776                         mst = os.lstat(self.dst_mntpt)
 777                         if not S_ISDIR(mst.st_mode):
 778                                 raise TValueError("Destination mountpoint "
 779                                     "doesn't exist", TM_E_INVALID_CPIO_ACT_ATTR)
 780                 except OSError:
 781                         raise TValueError("Destination mountpoint is "
 782                             "inaccessible", TM_E_INVALID_CPIO_ACT_ATTR)
 783 
 784                 #
 785                 # Read in approx size of the entire distribution from
 786                 # .image_info file. This is used for disk space usage
 787                 # monitoring.
 788                 #
 789                 if self.image_info:
 790                         try:
 791                                 ih = open(self.image_info, 'r')
 792                         except IOError:
 793                                 raise TAbort("Failed to access " +
 794                                     self.image_info, TM_E_INVALID_CPIO_ACT_ATTR)
 795 
 796                         for line in ih:
 797                                 (opt, val) = line.split("=")
 798                                 if opt == "IMAGE_SIZE":
 799                                         # Remove the '\n' character read from
 800                                         # the file, and convert to integer
 801                                         self.distro_size = int(val.rstrip('\n'))
 802                                         break
 803                         ih.close()
 804 
 805                         if (self.distro_size == 0):
 806                                 # We should have read in a size by now
 807                                 raise TAbort("Unable to read " \
 808                                     "IMAGE_SIZE in " + self.image_info,
 809                                     TM_E_INVALID_CPIO_ACT_ATTR)
 810 
 811                 try:
 812                         os.putenv('TMPDIR', '/tmp')
 813                 except:
 814                         pass
 815 
 816                 if self.cpio_action == TM_CPIO_ENTIRE:
 817                         self.cpio_transfer_entire_directory()
 818                 elif self.cpio_action == TM_CPIO_LIST:
 819                         try:
 820                                 open(self.list_file, 'r') 
 821                         except:
 822                                 raise TAbort("Unable to open " + self.list_file,
 823                                     TM_E_INVALID_CPIO_ACT_ATTR);
 824                         fent_list = []
 825                         fent = Flist()
 826                         fent_list.append(fent)
 827                         fent.name = self.list_file 
 828                         fent.chdir_prefix = self.src_mntpt
 829                         fent.cpio_args = self.cpio_args 
 830                         self.cpio_transfer_filelist(fent_list,
 831                             TM_E_CPIO_LIST_FAILED)
 832                 else:
 833                         raise TAbort("Invalid CPIO action",
 834                             TM_E_INVALID_CPIO_ACT_ATTR)
 835 
 836                 tmod.logprogress(100, "Complete transfer process")
 837                 self.info_msg("-- Completed transfer process, " +
 838                     time.strftime(self.tformat) + " --")
 839 
 840 class Transfer_ips(object):
 841         """This class contains all the methods used to create an IPS
 842         image and populate it
 843         """
 844 
 845         def __init__(self):
 846                 self._action = "" 
 847                 self._pkg_url = ""
 848                 self._pkg_auth = ""
 849                 self._init_mntpt = ""
 850                 self._pkgs_file = "" 
 851                 self.debugflag = 0 
 852                 self._image_type = "F"
 853                 self._image_create_force_flag = ""
 854                 self._alt_auth = ""
 855                 self._alt_url = ""
 856                 self._pref_flag = ""
 857                 self._mirr_flag = ""
 858                 self._no_index_flag = ""
 859                 self._refresh_flag = "--no-refresh"
 860                 self._prop_name = ""
 861                 self._prop_value = ""
 862                 self._log_handler = None
 863                 self._verbose_mode = ""
 864                 
 865         def prerror(self, msg):
 866                 """Log an error message to logging service and stderr
 867                 """
 868                 msg1 = msg + "\n"
 869                 logsvc.write_dbg(TRANSFER_ID, logsvc.LS_DBGLVL_ERR, msg1)
 870                 sys.stderr.write(msg1)
 871                 sys.stderr.flush()
 872 
 873         def perform_ips_init(self):
 874                 """Perform an IPS image-create call. 
 875                 Raises TAbort if unable to create the IPS image
 876                 """
 877                 # Check that the required values have been set.
 878                 if self._pkg_url == "":
 879                         raise TValueError("IPS repository not set",
 880                             TM_E_INVALID_IPS_ACT_ATTR)
 881 
 882                 if self._pkg_auth== "":
 883                         raise TValueError("IPS publisher not set",
 884                             TM_E_INVALID_IPS_ACT_ATTR)
 885 
 886                 # Generate the command to create the IPS image
 887                 cmd = TM_defs.PKG + " image-create %s -%s -p %s=%s %s" % \
 888                     (self._image_create_force_flag, self._image_type,
 889                     self._pkg_auth, self._pkg_url, self._init_mntpt)
 890 
 891                 try:
 892                         if (self._log_handler != None):
 893                                 status = exec_cmd_outputs_to_log(cmd.split(),
 894                                     self._log_handler)
 895                         else:
 896                                 status = call(cmd, shell=True)
 897                         if status:
 898                                 raise TAbort("Unable to initialize the "
 899                                     "pkg image area at "
 900                                     + self._init_mntpt, TM_E_IPS_INIT_FAILED)   
 901                 except OSError:
 902                         raise TAbort("Unable to initialize the pkg image area "
 903                             "at " + self._init_mntpt, TM_E_IPS_INIT_FAILED)     
 904 
 905         def perform_ips_repo_contents_verify(self):
 906                 """Verify the packages specified by the user are actually
 907                 contained in the repository they specify.
 908                 Raises TAbort if unable to verify the IPS repository
 909                 """
 910                 # Verifying that needed packages are in the repository...
 911                 # Fetching list of repository packages...
 912 
 913                 # Check that the required parameters are set.
 914                 if self._pkgs_file == "":
 915                         raise TValueError("IPS pkg list not set",
 916                             TM_E_INVALID_IPS_ACT_ATTR)
 917 
 918                 # Check that the init_mntpt really exists. If not, error.
 919                 try:
 920                         mst = os.lstat(self._init_mntpt)
 921                         if not S_ISDIR(mst.st_mode):
 922                                 raise TValueError("Specified IPS image area "
 923                                     "doesn't exist", TM_E_INVALID_IPS_ACT_ATTR)
 924                 except OSError:
 925                         raise TValueError("Specified IPS image area is "
 926                             "inaccessible", TM_E_INVALID_IPS_ACT_ATTR)
 927 
 928 
 929                 # Checking against list of requested packages..
 930                 try:
 931                         pkgfile = open(self._pkgs_file, 'r')
 932                 except:
 933                         raise TAbort("Unable to open the IPS packages file "
 934                             + self._pkgs_file,
 935                             TM_E_IPS_REPO_CONTENTS_VERIFY_FAILED)       
 936 
 937                 # For each package in our pkgs file, see if it's in the
 938                 # IPS repository.
 939                 pkglist = ""
 940                 for line in pkgfile:
 941                         pkglist += " " + line.rstrip()
 942                 pkgfile.close()
 943                 cmd = TM_defs.PKG + " -R %s list %s -a %s" %  \
 944                     (self._init_mntpt, self._verbose_mode, pkglist)
 945                 try:
 946                         if (self._log_handler != None):
 947                                 status = exec_cmd_outputs_to_log(cmd.split(),
 948                                     self._log_handler)
 949                         else:
 950                                 status = call(cmd, shell=True)
 951                         if status:
 952                                 raise TIPSPkgmissing(TM_E_IPS_PKG_MISSING)
 953                 except OSError:
 954                         raise TAbort("Unable to verify the contents of"
 955                             " the IPS repository",
 956                             TM_E_IPS_REPO_CONTENTS_VERIFY_FAILED)       
 957 
 958 
 959         def perform_ips_set_prop(self):
 960                 """Perform an IPS set-property of the property 
 961                 specified.
 962                 Raises: TAbort if unable to set property. 
 963                 """
 964 
 965                 # Check that the required parameters are set.
 966                 if self._prop_name == "":
 967                         raise TValueError("IPS property name not set",
 968                             TM_E_INVALID_IPS_ACT_ATTR)
 969 
 970                 if self._prop_value == "":
 971                         raise TValueError("IPS property value not set",
 972                             TM_E_INVALID_IPS_ACT_ATTR)
 973         
 974                 # Check that the init_mntpt really exists. If not, error.
 975                 try:
 976                         mst = os.lstat(self._init_mntpt)
 977                         if not S_ISDIR(mst.st_mode):
 978                                 raise TValueError("Specified IPS image area "
 979                                     "doesn't exist", TM_E_INVALID_IPS_ACT_ATTR)
 980                 except OSError:
 981                         raise TValueError("Specified IPS image area is "
 982                             "inaccessible", TM_E_INVALID_IPS_ACT_ATTR)
 983 
 984                 cmd = TM_defs.PKG + \
 985                     " -R %s set-property %s %s" % \
 986                     (self._init_mntpt, self._prop_name, self._prop_value)
 987 
 988                 try:
 989                         if (self._log_handler != None):
 990                                 status = exec_cmd_outputs_to_log(cmd.split(),
 991                                     self._log_handler)
 992                         else:
 993                                 status = call(cmd, shell=True)
 994                         if status:
 995                                 raise TAbort("Unable to set property", \
 996                                     TM_E_IPS_SET_PROP_FAILED)   
 997                 except OSError:
 998                         raise TAbort("Unable to set property",
 999                             TM_E_IPS_SET_PROP_FAILED)   
1000                 
1001         def perform_ips_set_auth(self):
1002                 """Perform an IPS set-publisher of the additional publisher
1003                 specified. By default, the --no-refresh flag is used
1004                 so the catalog doesn't get refreshed when set-publisher
1005                 is run.  If the caller wants the catalog to be refreshed,
1006                 specify the TM_IPS_REFRESH_CATALOG=true option when calling
1007                 this function.
1008                 Raises: TAbort if unable to set the additional publisher. 
1009                 """
1010 
1011                 # Check that the required parameters are set.
1012                 if self._alt_auth == "":
1013                         raise TValueError("IPS alternate publisher not set",
1014                             TM_E_INVALID_IPS_ACT_ATTR)
1015 
1016                 if self._alt_url == "":
1017                         raise TValueError("IPS alternate publisher url not set",
1018                             TM_E_INVALID_IPS_ACT_ATTR)
1019 
1020                 # Check that the init_mntpt really exists. If not, error.
1021                 try:
1022                         mst = os.lstat(self._init_mntpt)
1023                         if not S_ISDIR(mst.st_mode):
1024                                 raise TValueError("Specified IPS image area "
1025                                     "doesn't exist", TM_E_INVALID_IPS_ACT_ATTR)
1026                 except OSError:
1027                         raise TValueError("Specified IPS image area is "
1028                             "inaccesible", TM_E_INVALID_IPS_ACT_ATTR)
1029 
1030                 if self._pref_flag and self._mirr_flag:
1031                         raise TValueError("Unable to perform IPS set-publisher "
1032                             "with -p and -m flags in same transaction",
1033                             TM_E_INVALID_IPS_ACT_ATTR)
1034 
1035                 if self._mirr_flag:
1036                         cmd = TM_defs.PKG + \
1037                             " -R %s set-publisher %s %s %s %s" % \
1038                             (self._init_mntpt, self._mirr_flag, self._alt_url,
1039                             self._refresh_flag, self._alt_auth)
1040                 else:
1041                         cmd = TM_defs.PKG + \
1042                             " -R %s set-publisher %s -O %s %s %s" % \
1043                             (self._init_mntpt, self._pref_flag, self._alt_url,
1044                             self._refresh_flag, self._alt_auth)
1045                 try:
1046                         if (self._log_handler != None):
1047                                 status = exec_cmd_outputs_to_log(cmd.split(),
1048                                     self._log_handler)
1049                         else:
1050                                 status = call(cmd, shell=True)
1051                         if status:
1052                                 raise TAbort("Unable to set an additional " \
1053                                     "publisher", TM_E_IPS_SET_AUTH_FAILED)      
1054                 except OSError:
1055                         raise TAbort("Unable to set an additional publisher",
1056                             TM_E_IPS_SET_AUTH_FAILED)   
1057                 
1058 
1059         def perform_ips_refresh(self):
1060                 """Perform an IPS refresh if the image area 
1061                 Raises: TAbort if unable to refresh the IPS image 
1062                 """
1063 
1064                 # Check that the init_mntpt really exists. If not, error.
1065                 try:
1066                         mst = os.lstat(self._init_mntpt)
1067                         if not S_ISDIR(mst.st_mode):
1068                                 raise TValueError("Specified IPS image area "
1069                                     "doesn't exist", TM_E_INVALID_IPS_ACT_ATTR)
1070                 except OSError:
1071                         raise TValueError("Specified IPS image area is "
1072                             "inaccessible", TM_E_INVALID_IPS_ACT_ATTR)
1073 
1074 
1075                 cmd = TM_defs.PKG + " -R %s refresh" % self._init_mntpt
1076                 try:
1077                         if (self._log_handler != None):
1078                                 status = exec_cmd_outputs_to_log(cmd.split(),
1079                                     self._log_handler)
1080                         else:
1081                                 status = call(cmd, shell=True)
1082                         if status:
1083                                 raise TAbort("Unable to refresh the IPS image",
1084                                     TM_E_IPS_REFRESH_FAILED)    
1085                 except OSError:
1086                         raise TAbort("Unable to refresh the IPS image",
1087                             TM_E_IPS_REFRESH_FAILED)    
1088                 
1089 
1090         def perform_ips_unset_auth(self):
1091                 """Perform an IPS unset-publisher of the specified publisher 
1092                 Raises: TAbort if unable to unset the publisher 
1093                 """
1094 
1095                 # Check that the required parameters are set.
1096                 if self._alt_auth == "":
1097                         raise TValueError("IPS alternate publisher not set",
1098                             TM_E_INVALID_IPS_ACT_ATTR)
1099 
1100                 # Check that the init_mntpt really exists. If not, error.
1101                 try:
1102                         mst = os.lstat(self._init_mntpt)
1103                         if not S_ISDIR(mst.st_mode):
1104                                 raise TValueError("Specified IPS image area "
1105                                     "doesn't exist", TM_E_INVALID_IPS_ACT_ATTR)
1106                 except OSError:
1107                         raise TValueError("Specified IPS image area is "
1108                             "inaccessible", TM_E_INVALID_IPS_ACT_ATTR)
1109 
1110                 cmd = TM_defs.PKG +" -R %s unset-publisher %s" % \
1111                     (self._init_mntpt, self._alt_auth)
1112                 try:
1113                         if (self._log_handler != None):
1114                                 status = exec_cmd_outputs_to_log(cmd.split(),
1115                                     self._log_handler)
1116                         else:
1117                                 status = call(cmd, shell=True)
1118                         if status:
1119                                 raise TAbort("Unable to unset-publisher",
1120                                     TM_E_IPS_UNSET_AUTH_FAILED) 
1121                 except OSError:
1122                         raise TAbort("Unable to unset-publisher",
1123                             TM_E_IPS_UNSET_AUTH_FAILED) 
1124                 
1125 
1126         def perform_ips_pkg_op(self, action_str):
1127                 """Perform an IPS pkg install/uninstall of the packages
1128                 specified.
1129                 argument:
1130                         action_str: "install" indicates that this is for doing
1131                                 a "pkg install" of packages.  "uninstall"
1132                                 means this is for doing a "pkg uninstall"
1133                                 of packages.
1134                 Raises: TAbort if unable to install/uninstall the packages.
1135                 """
1136 
1137                 # make sure action_str is defined and it is a valid action
1138                 if ((action_str != "install") and (action_str != "uninstall")):
1139                         raise TValueError("Invalid action string: "
1140                             + action_str)
1141 
1142                 # Check that the required parameters are set.
1143                 if self._pkgs_file == "":
1144                         raise TValueError("IPS package file not set",
1145                             TM_E_INVALID_IPS_ACT_ATTR)
1146 
1147                 # Check that the init_mntpt really exists. If not, error.
1148                 try:
1149                         mst = os.lstat(self._init_mntpt)
1150                         if not S_ISDIR(mst.st_mode):
1151                                 raise TValueError("Specified IPS image area "
1152                                     "doesn't exist", TM_E_INVALID_IPS_ACT_ATTR)
1153                 except OSError:
1154                         raise TValueError("Specified IPS image area is "
1155                             "inaccessible", TM_E_INVALID_IPS_ACT_ATTR)
1156 
1157                 # Open the file that contains the packages to work on
1158                 try:
1159                         pkgfile = open(self._pkgs_file, 'r')
1160                 except IOError:
1161                         raise TAbort("Unable to read list of packages "
1162                             " to " + action_str, TM_E_IPS_RETRIEVE_FAILED)
1163 
1164                 # install/uninstall each package, keeping track if
1165                 # any are missing.
1166                 missingpkg = 0
1167                 for line in pkgfile:
1168                         cmd = (TM_defs.PKG + " -R %s %s %s %s %s") % \
1169                             (self._init_mntpt, action_str, self._verbose_mode,
1170                             self._no_index_flag, line)
1171                         try:
1172                                 if (self._log_handler != None):
1173                                         status = exec_cmd_outputs_to_log \
1174                                             (cmd.split(), self._log_handler)
1175                                 else:
1176                                         status = call(cmd, shell=True)
1177                                 if status:
1178                                         missingpkg = 1
1179                                         err_str = ("Unable to " + action_str + \
1180                                             " %s in %s") \
1181                                             % (str.rstrip(line),
1182                                             self._init_mntpt)
1183                                         if (self._log_handler != None):
1184                                                 self._log_handler.error(err_str)
1185                                         else:
1186                                                 print err_str
1187                         except OSError:
1188                                 raise TAbort("Unable to "
1189                                     + action_str + " %s in %s"
1190                                     % (line, self._init_mntpt),
1191                                     TM_E_IPS_RETRIEVE_FAILED)
1192 
1193                 pkgfile.close()
1194 
1195                 # If there was a missing package, raise an exception
1196                 # so the caller can decide what to do.
1197                 if missingpkg:
1198                         raise TIPSPkgmissing(TM_E_IPS_PKG_MISSING)
1199 
1200         def perform_ips_purge_hist(self):
1201                 """Perform an IPS pkg purge-history.
1202                 Raises: TAbort if unable to purge the history.
1203                 """
1204 
1205                 # Check that the init_mntpt really exists. If not, error.
1206                 try:
1207                         mst = os.lstat(self._init_mntpt)
1208                         if not S_ISDIR(mst.st_mode):
1209                                 raise TValueError("Specified IPS image area "
1210                                     "doesn't exist", TM_E_INVALID_IPS_ACT_ATTR)
1211                 except OSError:
1212                         raise TValueError("Specified IPS image area is "
1213                             "inaccessible", TM_E_INVALID_IPS_ACT_ATTR)
1214 
1215                 cmd = TM_defs.PKG + " -R %s purge-history" % \
1216                     (self._init_mntpt)
1217                 try:
1218                         if (self._log_handler != None):
1219                                 status = exec_cmd_outputs_to_log(cmd.split(),
1220                                     self._log_handler)
1221                         else:
1222                                 status = call(cmd, shell=True)
1223                         if status:
1224                                 raise TAbort("Unable to pkg purge-history "
1225                                     " the IPS image at " + self._init_mntpt)    
1226                 except OSError:
1227                         raise TAbort("Unable to pkg purge-history "
1228                             "the IPS image at " + self._init_mntpt,
1229                             TM_E_IPS_RETRIEVE_FAILED)
1230 
1231         def perform_transfer(self, args):
1232                 """Perform a transfer using IPS.
1233                 Input: args - specifies what IPS action to
1234                     perform, init, contents verify, retrieve/install,
1235                     set-publisher, refresh, or unset-publisher.
1236                 Raises: TAbort
1237                 """     
1238 
1239                 for opt, val in args:
1240                         if opt == TM_ATTR_MECHANISM:
1241                                 continue
1242                         elif opt == TM_IPS_ACTION:
1243                                 self._action = val
1244                         elif opt == TM_IPS_PKG_URL:
1245                                 self._pkg_url = val
1246                         elif opt == TM_IPS_PKG_AUTH:
1247                                 self._pkg_auth = val
1248                         elif opt == TM_IPS_INIT_MNTPT:
1249                                 self._init_mntpt = val
1250                         elif opt == TM_IPS_PKGS:
1251                                 self._pkgs_file = val
1252                         elif opt == TM_IPS_IMAGE_TYPE:
1253                                 self._image_type = val
1254                         elif opt == TM_IPS_IMAGE_CREATE_FORCE:
1255                                 if val == "true":
1256                                         self._image_create_force_flag = "-f"
1257                         elif opt == TM_IPS_VERBOSE_MODE:
1258                                 if val == "true":
1259                                         self._verbose_mode = "-v"
1260                         elif opt == TM_IPS_ALT_AUTH:
1261                                 self._alt_auth = val
1262                         elif opt == TM_IPS_ALT_URL:
1263                                 self._alt_url = val
1264                         elif opt == TM_IPS_PREF_FLAG:
1265                                 self._pref_flag = val
1266                         elif opt == TM_IPS_MIRROR_FLAG:
1267                                 self._mirr_flag = val
1268                         elif opt == TM_IPS_GENERATE_SEARCH_INDEX:
1269                                 # This is only used for install/uninstall
1270                                 # operations
1271                                 if val.lower() != "true":
1272                                         self._no_index_flag = "--no-index"
1273                         elif opt == TM_IPS_REFRESH_CATALOG:
1274                                 # This is only used for set-publisher
1275                                 if (self._action != TM_IPS_SET_AUTH):
1276                                         raise TValueError("Attribute "
1277                                             + str(opt) + "is only used " \
1278                                             "for set-publisher",
1279                                             TM_E_INVALID_TRANSFER_TYPE_ATTR)
1280                                 if val.lower() == "true":
1281                                         self._refresh_flag = ""
1282                         elif opt == TM_IPS_PROP_NAME:
1283                                 # The prop name has already been set. Only one
1284                                 # property name is allowed so error out.
1285                                 if self._prop_name != "":
1286                                         raise TValueError("Only one property "
1287                                             "can be set per call.", TM_E_INVALID_IPS_ACT_ATTR)
1288                                 self._prop_name = val
1289                         elif opt == TM_IPS_PROP_VALUE:
1290                                 # The prop value has already been set. Only one
1291                                 # property value is allowed so error out.
1292                                 if self._prop_value != "":
1293                                         raise TValueError("Only one property value"
1294                                             " can be specified per call.",
1295                                             TM_E_INVALID_IPS_ACT_ATTR)
1296                                 self._prop_value = val
1297                         elif opt == TM_PYTHON_LOG_HANDLER:
1298                                 self._log_handler = val
1299                         elif opt == "dbgflag":
1300                                 if val == "true":
1301                                         self.debugflag = 1
1302                                 else:
1303                                         self.debugflag = 0
1304                         else:
1305                                 raise TValueError("Invalid attribute " +
1306                                     str(opt), TM_E_INVALID_TRANSFER_TYPE_ATTR)
1307                         
1308                 if self._init_mntpt == "":
1309                         raise TValueError("Image mountpoint not set",
1310                             TM_E_INVALID_IPS_ACT_ATTR)
1311 
1312                 if self._action == "":
1313                         raise TValueError("TM_IPS_ACTION not set",
1314                             TM_E_INVALID_IPS_ACT_ATTR)
1315                 elif self._action == TM_IPS_INIT:
1316                         self.perform_ips_init()
1317                 elif self._action == TM_IPS_REPO_CONTENTS_VERIFY:
1318                         self.perform_ips_repo_contents_verify()
1319                 elif self._action == TM_IPS_RETRIEVE:
1320                         self.perform_ips_pkg_op("install")
1321                 elif self._action == TM_IPS_SET_AUTH:
1322                         self.perform_ips_set_auth()
1323                 elif self._action == TM_IPS_REFRESH:
1324                         self.perform_ips_refresh()
1325                 elif self._action == TM_IPS_UNSET_AUTH:
1326                         self.perform_ips_unset_auth()
1327                 elif self._action == TM_IPS_PURGE_HIST:
1328                         self.perform_ips_purge_hist()
1329                 elif self._action == TM_IPS_UNINSTALL:
1330                         self.perform_ips_pkg_op("uninstall")
1331                 elif self._action == TM_IPS_SET_PROP:
1332                         self.perform_ips_set_prop()
1333                 else:
1334                         raise TValueError("Invalid TM_IPS_ACTION",
1335                             TM_E_INVALID_IPS_ACT_ATTR)
1336 
1337 def tm_perform_transfer(args, callback=None):
1338         """Transfer data via cpio or IPS from a specified source to 
1339         destination. The cpio transfer can be either an entire directory
1340         or a list of files. The IPS functionality that is supported is
1341         image-create, content verification, set-publisher, refresh,
1342         unset-publisher, and retrieval.
1343         Arguments: nvlist specifying the transfer characteristics
1344                 callback function for logging.
1345         Returns: TM_E_SUCCESS
1346                  TM_E_IPS_PKG_MISSING
1347                  TM_E_IPS_RETRIEVE_FAILED
1348                  TM_E_IPS_SET_AUTH_FAILED
1349                  TM_E_IPS_UNSET_AUTH_FAILED
1350                  TM_E_IPS_REFRESH_FAILED
1351                  TM_E_IPS_REPO_CONTENTS_VERIFY_FAILED
1352                  TM_E_IPS_INIT_FAILED
1353                  TM_E_INVALID_CPIO_ACT_ATTR
1354                  TM_E_INVALID_CPIO_FILELIST_ATTR
1355         """
1356 
1357         # lock, so there isn't more than 1 transfer running at a time 
1358         params.tm_lock = threading.Lock()
1359 
1360         try:
1361                 params.tm_lock.acquire()
1362 
1363                 rv = TM_E_SUCCESS
1364 
1365                 # If the callback is specified, set the python
1366                 # callback function in the associated transfer mod
1367                 # C code.
1368                 if callback != None:
1369                         # Set the python callback function
1370                         tmod.set_py_callback(callback);
1371 
1372                 action = ""
1373                 for opt, val in args:
1374                         if opt == TM_ATTR_MECHANISM:
1375                                 action = val
1376                                 break
1377 
1378                 if action == TM_PERFORM_IPS:
1379                         tobj = Transfer_ips()
1380                 elif action == TM_PERFORM_CPIO:
1381                         tobj = Transfer_cpio()
1382                 else:
1383                         if params.tm_lock.locked():
1384                                 params.tm_lock.release()        
1385                         rv = TM_E_INVALID_TRANSFER_TYPE_ATTR
1386                         return rv
1387 
1388                 try:
1389                         tobj.perform_transfer(args)
1390                 except IOError, (errno, strerror):
1391                         tobj.prerror("File operation error: ")
1392                         tobj.prerror(traceback.format_exc())
1393                         logsvc.write_log(TRANSFER_ID, "IOERROR\n")
1394                         rv = TM_E_PYTHON_ERROR
1395 
1396                 except (TValueError, TAbort), v:
1397                         tobj.prerror(v.message)
1398                         logsvc.write_log(TRANSFER_ID, "TValueError or TABort\n")
1399                         rv = v.retcode
1400 
1401                 except TIPSPkgmissing, v:
1402                         logsvc.write_log(TRANSFER_ID, "pkg missing\n")
1403                         rv = v.retcode
1404 
1405                 except:
1406                         logsvc.write_log(TRANSFER_ID, "everything else\n")
1407                         tobj.prerror(traceback.format_exc())
1408                         rv = TM_E_PYTHON_ERROR
1409 
1410         finally:
1411                 if params.tm_lock.locked():
1412                         params.tm_lock.release()        
1413 
1414         return rv
1415 
1416 # global parameters 
1417 params = TM_defs()
1418 # Grab defines from transfermod.h
1419 execfile('/usr/lib/python2.4/vendor-packages/osol_install/transfer_defs.py')