/var/lib/sorcery/modules/libtrack

     1	#!/bin/bash
     2	#---------------------------------------------------------------------
     3	## @Synopsis Functions for dealing with tracking of files, and other installwatch related things.
     4	## @Copyright Copyright (C) 2004 The Source Mage Team <http://www.sourcemage.org>
     5	## Functions for dealing with tracking of files, and other installwatch related things.
     6	#---------------------------------------------------------------------
     7	
     8	#---------------------------------------------------------------------
     9	##
    10	##  Initialize libtrack, currently this is just the modified files list.
    11	##  This is used by cast to exclude files it's modified from the md5 list.
    12	##
    13	#---------------------------------------------------------------------
    14	function libtrack_init() {
    15	  [[ $__MODIFIED_FILES ]] || export __MODIFIED_FILES="$TMP_DIR/modified_files"
    16	}
    17	
    18	
    19	#---------------------------------------------------------------------
    20	## @Stdin list of files
    21	## @Stdout list of files
    22	## Reads a list of files from standard in, and returns a list of the
    23	## files that exist.
    24	##
    25	#---------------------------------------------------------------------
    26	function exists()  {
    27	  local item
    28	  while read item; do
    29	    [[ -e $item ]] && echo $item
    30	  done
    31	}
    32	
    33	
    34	#---------------------------------------------------------------------
    35	##
    36	## Given a list of files it will notify installwatch of them.
    37	## Useful for spells whose components are not dynamically linked
    38	## to glibc. Uses simple hack of touching files while
    39	## installwatch is running.
    40	##
    41	#---------------------------------------------------------------------
    42	function real_track_manual()  {
    43	  if  [[ -z "$STAGED_INSTALL" || $STAGED_INSTALL == off ]] &&
    44	      [[ -z "$INSTALLWATCHFILE" && -z "$INSTW_LOGFILE" ]] ;  then
    45	    echo "Can't tell installwatch to manually track... installwatch isn't running."
    46	    return 1
    47	  fi
    48	  touch -c "$@"
    49	  return 0
    50	}
    51	
    52	#--------------------------------------------------------------------
    53	## Some simple castfs sanity checking
    54	## make a file, check to see if it exists, check to see if the
    55	## contents of the file is what we made it
    56	#--------------------------------------------------------------------
    57	function run_castfs_sanity()
    58	{
    59	  export CASTFS_LOGFILE=${CASTFS_DBGLOG}
    60	  export CASTFS_DBGLVL=${CASTFS_DEBUG_LEVEL}
    61	  debug  "libtrack" "$FUNCNAME on $SPELL"
    62	
    63	  # first check if /dev/null is really a character device #13315
    64	  if [[ ! -c /dev/null ]]; then
    65	    debug "libtrack" "$FUNCNAME failure: damaged /dev/null"
    66	    return 1
    67	  fi
    68	
    69	  mkdir -p "${TMP_DIR}/test-mount" &&
    70	  mkdir -p "${TMP_DIR}/test-stage" &&
    71	  castfs "${TMP_DIR}/test-mount" -o "stage=${TMP_DIR}/test-stage" -o "ignore=/tmp" &&
    72	  pushd "${TMP_DIR}/test-mount" > /dev/null &&
    73	  (
    74	    (echo stuff > foo) &&
    75	    (ls foo > /dev/null 2>&1) &&
    76	    (test "stuff" = "`cat foo`") &&
    77	    rm -f foo
    78	  )
    79	  local rc=$?
    80	  popd > /dev/null
    81	  if [[ $rc == 0 ]]
    82	  then
    83	    debug "libtrack" "$FUNCNAME success"
    84	  else
    85	    debug "libtrack" "$FUNCNAME failure"
    86	  fi
    87	  umount -l "${TMP_DIR}/test-mount"
    88	  return $rc
    89	}
    90	
    91	#---------------------------------------------------------------------
    92	##
    93	## Starts Translation Stage Root
    94	##
    95	#---------------------------------------------------------------------
    96	function real_invoke_stage_root()
    97	{
    98	  export CASTFS_LOGFILE=${CASTFS_DBGLOG}
    99	  export CASTFS_DBGLVL=${CASTFS_DEBUG_LEVEL}
   100	  local CASTFS_IGNORE_LIST=""
   101	  local LOCAL_IGNORE_DIRS="${CCACHE_DIR} ${DISTCC_DIR}"
   102	  # this forks and daemonizes so it will exit you simply unmount the dir to stop the process
   103	  for dir in ${CASTFS_UNSTAGED_PATHS} ${LOCAL_IGNORE_DIRS}
   104	  do
   105	    if [[ -h $dir ]]; then
   106	      CASTFS_IGNORE_LIST="${CASTFS_IGNORE_LIST} -o ignore=$(readlink $dir)"
   107	    elif [[ -d $dir ]]; then
   108	      CASTFS_IGNORE_LIST="${CASTFS_IGNORE_LIST} -o ignore=$dir"
   109	    else
   110	      debug "libtrack" "User wanted $FUNCNAME to treat $dir as a directory!"
   111	    fi
   112	  done &&
   113	  castfs "${STAGE_DIRECTORY}/MOUNT" -o "stage=${STAGE_DIRECTORY}/TRANSL" $CASTFS_IGNORE_LIST &&
   114	  for dir in /dev /dev/pts /proc /sys
   115	  do
   116	    mount -o bind "$dir" "${STAGE_DIRECTORY}/MOUNT$dir"
   117	  done
   118	}
   119	
   120	#---------------------------------------------------------------------
   121	##
   122	## Stops Translation Stage Root
   123	##
   124	#---------------------------------------------------------------------
   125	function real_devoke_stage_root()
   126	{
   127	  unset CASTFS_LOGFILE
   128	  unset CASTFS_DBGLVL
   129	  for dir in /dev/pts /dev /proc /sys
   130	  do
   131	    umount -l "${STAGE_DIRECTORY}/MOUNT$dir"
   132	  done &&
   133	  umount -l "${STAGE_DIRECTORY}/MOUNT"
   134	}
   135	
   136	#---------------------------------------------------------------------
   137	##
   138	## Prepare Stage Root
   139	##
   140	#---------------------------------------------------------------------
   141	function prepare_stage_root()
   142	{
   143	  mk_source_dir "${STAGE_DIRECTORY}" &&
   144	  mk_source_dir "${STAGE_DIRECTORY}/TRANSL" &&
   145	  mk_source_dir "${STAGE_DIRECTORY}/MOUNT"
   146	}
   147	
   148	#---------------------------------------------------------------------
   149	##
   150	## Destroy Stage Root
   151	##
   152	#---------------------------------------------------------------------
   153	function destroy_stage_root()
   154	{
   155	  rm_source_dir $STAGE_DIRECTORY
   156	}
   157	
   158	#---------------------------------------------------------------------
   159	##
   160	## Sets up installwatch.
   161	##
   162	#---------------------------------------------------------------------
   163	function real_invoke_installwatch()  {
   164	  if [[ -e $INSTALLWATCH_SO ]]; then
   165	    export  INSTALLWATCHFILE=$IW_LOG
   166	    export  INSTW_LOGFILE=$IW_LOG
   167	    export  LD_PRELOAD=$INSTALLWATCH_SO
   168	  fi
   169	}
   170	
   171	#---------------------------------------------------------------------
   172	##
   173	## Stops using installwatch
   174	##
   175	#---------------------------------------------------------------------
   176	function real_devoke_installwatch()  {
   177	  unset  LD_PRELOAD
   178	  unset  INSTALLWATCHFILE INSTW_LOGFILE
   179	}
   180	
   181	function is_castfs_installed()
   182	{
   183	  local loc
   184	  if smgl_which castfs loc >/dev/null 2>&1 &&
   185	     (
   186	       modprobe fuse > /dev/null 2>&1 ||
   187	       grep -q '^nodev[[:space:]]*fuse$' /proc/filesystems
   188	     ) &&
   189	     [[ -c /dev/fuse ]]
   190	  then
   191	    message "${MESSAGE_COLOR}Staging enabled${DEFAULT_COLOR}"
   192	    return 0
   193	  else
   194	    message "${MESSAGE_COLOR}Staging disabled${DEFAULT_COLOR}"
   195	    return 1
   196	  fi
   197	}
   198	
   199	#---------------------------------------------------------------------
   200	##
   201	## Parses the installwatch log for files installed by a spell.
   202	##
   203	#---------------------------------------------------------------------
   204	function parse_iw()  {
   205	  local INPUT=$1
   206	
   207	  # it is EXTREMELY IMPORTANT that this variable contains an actual
   208	  # tab character and not some number of spaces. Otherwise BAD THINGS
   209	  # will happen.
   210	  local TAB=$'\t'
   211	  OMIT_IN="${TAB}rename\|${TAB}symlink\|${TAB}unlink\|${TAB}access\|${TAB}utimes"
   212	
   213	  grep -v "$OMIT_IN" $INPUT | cut -f3 | grep "^/" | sed 's#^//#/#g'
   214	  cat                $INPUT | cut -f4 | grep "^/" | sed 's#^//#/#g'
   215	}
   216	
   217	
   218	#---------------------------------------------------------------------
   219	##
   220	## Creates the install log containing all files installed by the spell.
   221	##
   222	#---------------------------------------------------------------------
   223	function create_install_log()  {
   224	  debug  "libtrack" "$FUNCNAME on $SPELL"
   225	  local INPUT=$1
   226	  local OUTPUT=$2
   227	
   228	  rm -f $OUTPUT
   229	  if [[ $STAGED_INSTALL != off ]]
   230	  then
   231	    get_all_package_files               |
   232	    filter_excluded                     > $OUTPUT
   233	  else
   234	    parse_iw $INPUT                     |
   235	    sed "s#/\(\./\)\+#/#g"              |
   236	    sort -u                             |
   237	    install_log_filter $INSTALL_ROOT "" |
   238	    grep -v -x ""                       |
   239	    filter_excluded                     |
   240	    install_log_filter "" $INSTALL_ROOT |
   241	    exists                              >  $OUTPUT
   242	  fi
   243	
   244	  echo "$C_LOG_COMP"                  >> $OUTPUT
   245	  echo "$MD5_LOG"                     >> $OUTPUT
   246	  echo "$INST_LOG"                    >> $OUTPUT
   247	
   248	}
   249	
   250	#---------------------------------------------------------------------
   251	##
   252	## Creates the install log containing all staged files. It reuses
   253	## the existing install log and just prepends $STAGE_DIRECTORY/TRANSL
   254	## to every line
   255	##
   256	## @param input log file
   257	## @param output log file
   258	##
   259	#---------------------------------------------------------------------
   260	function create_stage_install_log()  {
   261	  debug  "libtrack" "$FUNCNAME on $SPELL"
   262	  local input="$1"
   263	  local output="$2"
   264	
   265	  # treat the logs specially, as they weren't staged
   266	  grep -v "$C_LOG_COMP\|$MD5_LOG\|$INST_LOG" "$input" > "$output"
   267	  sed -i "s#^#$STAGE_DIRECTORY/TRANSL#" "$output"
   268	  echo "$C_LOG_COMP"                 >> "$output"
   269	  echo "$MD5_LOG"                    >> "$output"
   270	  echo "$INST_LOG"                   >> "$output"
   271	
   272	}
   273	
   274	#---------------------------------------------------------------------
   275	## Makes a list of files with the md5sum
   276	#---------------------------------------------------------------------
   277	function create_md5list() {
   278	  local INPUT=$1
   279	  local OUTPUT=$2
   280	  debug  "libtrack" "$FUNCNAME on $SPELL"
   281	
   282	  [[ $__MODIFIED_FILES ]] || export __MODIFIED_FILES="$TMP_DIR/modified_files"
   283	  touch $__MODIFIED_FILES
   284	  filter "$__MODIFIED_FILES" < $INPUT | while read LINE ; do
   285	    debug "libtrack" "Checking file $LINE"
   286	    if  [ -f "$LINE" ] ; then
   287	      debug "libtrack" "Running md5 on $LINE"
   288	      md5sum "$LINE"
   289	    fi
   290	  done 2>/dev/null > $OUTPUT
   291	}
   292	
   293	#---------------------------------------------------------------------
   294	## External api to note config files
   295	#---------------------------------------------------------------------
   296	function real_note_config_file() {
   297	  if check_if_modified "$1"; then
   298	    mark_file_modified "$1"
   299	  fi
   300	}
   301	
   302	
   303	#---------------------------------------------------------------------
   304	## Notes that a file was previously modified so that its md5 is
   305	## deliberatly munged
   306	#---------------------------------------------------------------------
   307	function mark_file_modified() {
   308	  [[ "$1" ]] || return 1
   309	  [[ $__MODIFIED_FILES ]] || export __MODIFIED_FILES="$TMP_DIR/modified_files"
   310	  echo "^$1\$" >> $__MODIFIED_FILES
   311	}
   312	
   313	
   314	#---------------------------------------------------------------------
   315	## @param file to check
   316	## @param md5 file (optional)
   317	#---------------------------------------------------------------------
   318	function check_if_modified() {
   319	  local to=$1
   320	  local md5_log=$2
   321	  if ! [[ $2 ]] ; then
   322	    md5_log="$TMP_DIR/$SPELL.md5"
   323	    if [[ $OLD_SPELL_VERSION ]] ; then
   324	      old_md5_log="$MD5SUM_LOGS/$SPELL-$OLD_SPELL_VERSION"
   325	      # log must be in filterable form
   326	      log_adjuster "$old_md5_log" "$md5_log" filterable root
   327	    else
   328	      old_md5_log=/dev/null
   329	    fi
   330	  fi
   331	  local my_md5=$(md5sum "$to")
   332	  if test -f "$md5_log" && grep -qx "$my_md5" "$md5_log"; then
   333	    false
   334	  else
   335	    true
   336	  fi
   337	}
   338	
   339	#---------------------------------------------------------------------
   340	## Constructs the to-be cache name depending on the ARCHIVEBIN
   341	## @param archive name
   342	## @param upvar
   343	#---------------------------------------------------------------------
   344	function construct_cache_name() {
   345	  local name=$1
   346	  if [[ -n $ARCHIVEBIN ]]; then
   347	    # just use the bin, currently there is no need for another extension
   348	    name="$name.$ARCHIVEBIN"
   349	  fi
   350	  upvar $2 "$name"
   351	}
   352	
   353	#---------------------------------------------------------------------
   354	## Given a filename, will return the actual filename if a similar
   355	## filename with a different extension exists. A more thorough version
   356	## of guess_filename used for finding caches
   357	## @param archive name (can be right trimmed, globbing will be done)
   358	## @param upvar (optional)
   359	#---------------------------------------------------------------------
   360	function find_cache() {
   361	  debug "libtrack" "$FUNCNAME $@"
   362	  local filename=$1
   363	  local real_name
   364	
   365	  if [[ -f  $filename ]]; then
   366	    real_name=$filename
   367	  else
   368	    # use the first if more were found
   369	    local basename
   370	    smgl_basename "$filename" basename
   371	    read real_name < <(find $INSTALL_CACHE -type f -name "$basename*")
   372	  fi
   373	  [[ -z $real_name ]] && return 1
   374	  debug "libtrack" "$FUNCNAME found $real_name"
   375	
   376	  if [[ -z $2 ]]; then
   377	    echo $real_name
   378	  else
   379	    upvar $2 "$real_name"
   380	  fi
   381	  return 0
   382	}
   383	
   384	#---------------------------------------------------------------------
   385	##
   386	## Creates a bzip/gzip'ed tar file containing an archived backup of
   387	## file specified on standard input into the target dir.
   388	##
   389	## Input files are relative to install root for regular files and
   390	## state root for state files
   391	## @param files, one per line, to put into the archive
   392	## @param archive name
   393	## @param compressed archive name
   394	##
   395	#---------------------------------------------------------------------
   396	function create_cache_archive()  {
   397	
   398	  debug  "libtrack" "$FUNCNAME on $SPELL"
   399	  if    [  "$ARCHIVE"  ==  "off"  ]; then
   400	    debug "libtrack" "$FUNCNAME - ARCHIVE=$ARCHIVE, aborting archival."
   401	    return
   402	  fi
   403	  debug "libtrack" "$FUNCNAME - ARCHIVE=$ARCHIVE, archiving."
   404	  local input=$1
   405	  local CACHE=$2
   406	  local CACHE_COMP=$3
   407	
   408	  message  "${MESSAGE_COLOR}Creating cache file" \
   409	           "${FILE_COLOR}${CACHE_COMP}${DEFAULT_COLOR}"
   410	  # gather the queuing factors, so we can include them in the label, saving us the
   411	  # need to compute the spell name and a few untars when dealing with just a cache
   412	  local label
   413	  label="$SPELL ${VERSION:-0} ${PATCHLEVEL:-0} ${SECURITY_PATCH:-0} ${UPDATED:-0}"
   414	
   415	  local TMP_DATA=$TMP_DIR/foo.data
   416	  local TMP_MDATA=$TMP_DIR/foo.mdata
   417	  seperate_state_files $input $TMP_DATA $TMP_MDATA
   418	
   419	  case "$ARCHIVEBIN" in
   420	    tar)
   421	      pushd $STATE_ROOT/ &>/dev/null
   422	      install_log_filter $STATE_ROOT "." < $TMP_MDATA |
   423	      tar --no-recursion -cPf "$CACHE" -T - -V "$label"
   424	      popd &>/dev/null
   425	
   426	      pushd $INSTALL_ROOT/ &>/dev/null
   427	      install_log_filter $INSTALL_ROOT "." < $TMP_DATA |
   428	      tar --no-recursion -rPf "$CACHE" -T -
   429	      popd &>/dev/null
   430	      ;;
   431	  esac
   432	  rm $TMP_DATA $TMP_MDATA
   433	
   434	  case "$COMPRESSBIN" in
   435	    gzip|bzip2|pbzip2|xz)
   436	      $COMPRESSBIN -c $CACHE > $CACHE_COMP
   437	      rm $CACHE
   438	      ;;
   439	  esac
   440	}
   441	
   442	#---------------------------------------------------------------------
   443	## this is to filter the install log from one form to another
   444	## for install_root/track_root conversions
   445	#---------------------------------------------------------------------
   446	function install_log_filter() {
   447	  sed "s:^$1:$2:"
   448	}
   449	function md5_log_filter() {
   450	  sed "s:  $1:  $2:"
   451	}
   452	
   453	#---------------------------------------------------------------------
   454	## @param input file (can be /dev/stdin)
   455	## @param output file (can be /dev/stdout)
   456	## @param input format (root/log/filterable)
   457	## @param output format (root/log/filterable)
   458	## @param filter callback (optional install_log_filter, could be md5_log_filter)
   459	##
   460	## This filters an install log from a given format into another format
   461	## <pre>
   462	## root: relative to / all paths are relative to / file existence tests
   463	## should work, INSTALL_ROOT and STATE_ROOT are prepended to data and
   464	## state files respectively
   465	##
   466	## log: relative to track_root etc, format used in the logs (see note on
   467	## special behavior below)
   468	##
   469	## filterable: track_root/install_root/state_root stripped out files can
   470	## have filters applied to them
   471	##
   472	## "Special" handling applies depending on whether STATE_ROOT is inside
   473	## or outside INSTALL_ROOT.
   474	## For converting into log format:
   475	## If STATE_ROOT is within INSTALL_ROOT
   476	## eg: STATE_ROOT=/opt/stuff INSTALL_ROOT=/opt/stuff
   477	## or  STATE_ROOT=/opt/stuff/state INSTALL_ROOT=/opt/stuff
   478	## the portion of INSTALL_ROOT within STATE_ROOT is replaced with TRACK_ROOT
   479	## if STATE_ROOT is outside of INSTALL_ROOT (eg /opt/stuff and /opt/state)
   480	## then STATE_ROOT is left as is
   481	##
   482	## Converting from log to root format is the inverse, and of course going
   483	## to filterable format just requires removing whatever the expected prefix is.
   484	## </pre>
   485	#---------------------------------------------------------------------
   486	function log_adjuster() {
   487	  local input=$1
   488	  local output=$2
   489	  local informat=$3
   490	  local outformat=$4
   491	  local callback=${5:-install_log_filter}
   492	
   493	  local data_in data_out metadata_in metadata_out cat_metadata
   494	
   495	
   496	  if [[ "$informat" == root ]] ; then
   497	    data_in=$INSTALL_ROOT
   498	    if [[ "$outformat" == log ]] ; then
   499	      # root to log
   500	      data_out=$TRACK_ROOT
   501	
   502	      # if the STATE_ROOT is within the install root, then the state files are
   503	      # adjusted relative to track_root, otherwise they are left as is
   504	      if ! [[ ${STATE_ROOT##$INSTALL_ROOT*} ]] ; then
   505	        metadata_in=$STATE_ROOT
   506	        metadata_out=$TRACK_ROOT${STATE_ROOT##$INSTALL_ROOT}
   507	      else
   508	        cat_metadata=yes
   509	      fi
   510	    elif [[ $outformat == filterable ]] ; then
   511	      # root to filterable
   512	      data_out=""
   513	      metadata_in=$STATE_ROOT
   514	      metadata_out=""
   515	    fi
   516	  elif [[ "$informat" == log ]] ; then
   517	    data_in=$TRACK_ROOT
   518	    if [[ "$outformat" == root ]] ; then
   519	      # log to root
   520	      data_out=$INSTALL_ROOT
   521	      if ! [[ ${STATE_ROOT##$INSTALL_ROOT*} ]] ; then
   522	        # we actually could do this another way by stripping off
   523	        # $TRACK_ROOT${STATE_ROOT##$INSTALL_ROOT}, and replacing
   524	        # it with $STATE_ROOT, but i think below is simpler and equivalent
   525	        metadata_in=$TRACK_ROOT
   526	        metadata_out=$INSTALL_ROOT
   527	      else
   528	        cat_metadata=yes
   529	      fi
   530	    elif [[ "$outformat" == filterable ]] ; then
   531	      # log to filterable
   532	      data_out=""
   533	      metadata_out=""
   534	      if ! [[ ${STATE_ROOT##$INSTALL_ROOT*} ]] ; then
   535	        metadata_in=$TRACK_ROOT${STATE_ROOT##$INSTALL_ROOT}
   536	      else
   537	        metadata_in=$STATE_ROOT
   538	      fi
   539	    fi
   540	  elif [[ "$informat" == filterable ]] ; then
   541	    data_in=""
   542	    metadata_in=""
   543	    if [[ "$outformat" == root ]] ; then
   544	      # filterable to root
   545	      data_out=$INSTALL_ROOT
   546	      metadata_out=$STATE_ROOT
   547	    elif [[ "$outformat" == log ]] ; then
   548	      # filterable to log
   549	      data_out=$TRACK_ROOT
   550	      if ! [[ ${STATE_ROOT##$INSTALL_ROOT*} ]] ; then
   551	        metadata_out=$TRACK_ROOT${STATE_ROOT##$INSTALL_ROOT}
   552	      else
   553	        metadata_out=$STATE_ROOT
   554	      fi
   555	    fi
   556	  fi
   557	
   558	  local TMP_SSF=$(make_safe_dir)
   559	  local TMP_DATA=$TMP_SSF/foo.data
   560	  local TMP_MDATA=$TMP_SSF/foo.mdata
   561	
   562	  seperate_state_files $input $TMP_DATA $TMP_MDATA
   563	  {
   564	    if [[ $cat_metadata ]] ; then
   565	      cat $TMP_MDATA
   566	    else
   567	      eval "$callback \"$metadata_in\" \"$metadata_out\" < $TMP_MDATA"
   568	    fi
   569	    eval "$callback \"$data_in\" \"$data_out\" < $TMP_DATA"
   570	  } > $output
   571	  rm $TMP_DATA $TMP_MDATA
   572	  rmdir $TMP_SSF
   573	  return 0
   574	}
   575	
   576	#---------------------------------------------------------------------
   577	## Split a log file into data that should be TRACK_ROOT'd versus
   578	## STATE_ROOT'd.
   579	##
   580	## @param filename or - (or /dev/stdin) for a pipe. This routine will read the input only once in-order to work with pipes.
   581	## @param filename for non-state files, possibly /dev/stdout or /dev/stderr
   582	## @param filename for state files, possibly /dev/stdout or /dev/stderr don't use the same stream for both types.
   583	##
   584	#---------------------------------------------------------------------
   585	function seperate_state_files() {
   586	  local REAL_LOG_DIR=${LOG_DIRECTORY#$STATE_ROOT}
   587	  local REAL_STATE_DIR=${STATE_DIRECTORY#$STATE_ROOT}
   588	
   589	  # the input file is almost certainly a pipe, and things get weird
   590	  # since we have to grep twice, so just dump the data into a unique file
   591	
   592	  local TMP_SSF=$(make_safe_dir)
   593	  local FILE=$TMP_SSF/ssf
   594	  cat $1 > $FILE
   595	  grep -v "$REAL_LOG_DIR\|$REAL_STATE_DIR" $FILE | grep -xv '' > $2
   596	  grep    "$REAL_LOG_DIR\|$REAL_STATE_DIR" $FILE | grep -xv '' > $3
   597	  rm $FILE
   598	  rmdir $TMP_SSF
   599	  return 0
   600	}
   601	
   602	#---------------------------------------------------------------------
   603	## Try to make a unique directory using $RANDOM, leverage the fact that
   604	## two simultaneous mkdir's will have one succeed and the other fail.
   605	##
   606	## This is run from log_adjuster primarily which may be invoked several
   607	## times in a pipe, when this happens, bash does a fork, but does not
   608	## seem to reseed the random number generator, causing a high rate of
   609	## collisions. This is not easily reproducable outside of sorcery at
   610	## the time of writing, but inside it happens nearly everytime with bash
   611	## 3.1. This may actually be a bash 3.1 bug.
   612	##
   613	## The collisions aren't bad necessarily, but they result in un-necesary
   614	## delay.
   615	##
   616	## Setting RANDOM re-seeds the random number generator.
   617	##
   618	## Despite the fact that the subshell and invocation of date are slow
   619	## the consequence for not doing them are worse. The nano-seconds
   620	## are usually going to be different between forks so the liklihood
   621	## of a collision is greatly reduced.
   622	#---------------------------------------------------------------------
   623	function make_safe_dir() { (
   624	  RANDOM=$(date "+%N")
   625	  local TMP_SSF=$TMP_DIR/$RANDOM
   626	  while ! mkdir "$TMP_SSF" &> /dev/null; do
   627	    RANDOM=$(date "+%N")
   628	    TMP_SSF=$TMP_DIR/$RANDOM
   629	    sleep .1
   630	    debug "libtrack" "safe dir collision on $TMP_SSF"
   631	  done
   632	  echo $TMP_SSF
   633	  return 0
   634	) }
   635	
   636	#---------------------------------------------------------------------
   637	## @License
   638	##
   639	## This software is free software; you can redistribute it and/or modify
   640	## it under the terms of the GNU General Public License as published by
   641	## the Free Software Foundation; either version 2 of the License, or
   642	## (at your option) any later version.
   643	##
   644	## This software is distributed in the hope that it will be useful,
   645	## but WITHOUT ANY WARRANTY; without even the implied warranty of
   646	## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   647	## GNU General Public License for more details.
   648	##
   649	## You should have received a copy of the GNU General Public License
   650	## along with this software; if not, write to the Free Software
   651	## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   652	##
   653	#---------------------------------------------------------------------