1#!/usr/bin/ksh93 2 3# 4# CDDL HEADER START 5# 6# The contents of this file are subject to the terms of the 7# Common Development and Distribution License (the "License"). 8# You may not use this file except in compliance with the License. 9# 10# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 11# or http://www.opensolaris.org/os/licensing. 12# See the License for the specific language governing permissions 13# and limitations under the License. 14# 15# When distributing Covered Code, include this CDDL HEADER in each 16# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 17# If applicable, add the following below this CDDL HEADER, with the 18# fields enclosed by brackets "[]" replaced with your own identifying 19# information: Portions Copyright [yyyy] [name of copyright owner] 20# 21# CDDL HEADER END 22# 23 24# 25# Copyright 2008 Sun Microsystems, Inc. All rights reserved. 26# Use is subject to license terms. 27# 28 29# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant 30export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin 31 32# Make sure all math stuff runs in the "C" locale to avoid problems 33# with alternative # radix point representations (e.g. ',' instead of 34# '.' in de_DE.*-locales). This needs to be set _before_ any 35# floating-point constants are defined in this script). 36if [[ "${LC_ALL}" != "" ]] ; then 37 export \ 38 LC_MONETARY="${LC_ALL}" \ 39 LC_MESSAGES="${LC_ALL}" \ 40 LC_COLLATE="${LC_ALL}" \ 41 LC_CTYPE="${LC_ALL}" 42 unset LC_ALL 43fi 44export LC_NUMERIC=C 45 46function fatal_error 47{ 48 print -u 2 "${progname}: $@" 49 exit 1 50} 51 52 53function usage 54{ 55 OPTIND=0 56 getopts -a "${progname}" "${multifollow_usage}" OPT '-?' 57 exit 2 58} 59 60# program start 61builtin basename 62builtin cat 63 64typeset progname="$(basename "${0}")" 65 66typeset -r multifollow_usage=$'+ 67[-?\n@(#)\$Id: multifollow (Roland Mainz) 2008-10-14 \$\n] 68[-author?Roland Mainz <roland.mainz@nrubsig.org>] 69[+NAME?multifollow - use tail -f on multiple files] 70[+DESCRIPTION?\bmultifollow\b is a small utilty which can "follow" multiple 71 files similar to tail -f.] 72 73[ file ... ] 74 75[+SEE ALSO?\bksh93\b(1), \btail\b(1)] 76' 77 78while getopts -a "${progname}" "${multifollow_usage}" OPT ; do 79# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" 80 case ${OPT} in 81 *) usage ;; 82 esac 83done 84shift $((OPTIND-1)) 85 86# expecting at least one more arguments 87(($# >= 1)) || usage 88 89builtin -f libshell.so.1 poll || fatal_error "poll builtin not found." 90 91typeset -a files 92integer numfiles=0 93integer i 94 95# register trap to cleanup child processes 96trap 'for ((i=0 ; i < numfiles ; i++ )) ; do kill -TERM ${files[i].childpid} ; done' EXIT 97 98# setup "tail -f" childs, FIFOs and information for the "poll" builtin 99for (( ; $# > 0 ; numfiles++ )) ; do 100 typeset files[${numfiles}]=( 101 typeset name="$1" 102 typeset pipename="/tmp/multifollow_pipe_${PPID}_$$_${numfiles}" 103 integer childpid=-1 104 105 # poll(1) information 106 integer fd="-1" 107 typeset events="POLLIN" 108 typeset revents="" 109 ) 110 111 mkfifo "${files[${numfiles}].pipename}" 112 redirect {files[numfiles].fd}<>"${files[numfiles].pipename}" 113 114 tail -f "${files[${numfiles}].name}" >"${files[${numfiles}].pipename}" & 115 files[${numfiles}].childpid=$! 116 117 rm "${files[${numfiles}].pipename}" 118 119 shift 120done 121 122typeset do_poll=true 123 124# event loop 125while true ; do 126 if ${do_poll} ; then 127 for ((i=0 ; i < numfiles ; i++ )) ; do 128 files[i].revents="" 129 done 130 poll files 131 fi 132 do_poll=true 133 134 for ((i=0 ; i < numfiles ; i++ )) ; do 135 if [[ "${files[i].revents}" != "" ]] ; then 136 # todo: investigate why we have to use "do_poll" at all - AFAIK it 137 # should be sufficient to call "poll" and get "revents" set if there 138 # are any remaining data... 139 if read -t0 -u${files[i].fd} line ; then 140 print -- "#${i}: ${line}" 141 do_poll=false 142 fi 143 fi 144 done 145done 146 147fatal_error "not reached." 148# EOF. 149