/* * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2008 Apple Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ * */ /* 8.30.99 - changes for linux version - IsProcessRunning changed - fputs difference. 8.2.99 rt - changed BCasterTracker::BCasterTracker to 5 second open timer. - no longer lists broadcasts that are not running. file is cleaned up on next "stop" */ #include "BCasterTracker.h" #include "MyAssert.h" #include "Trim.h" #include "GetWord.h" #include "OSThread.h" #include #include "SafeStdLib.h" #include #include #include #include #include #include #if !(defined(__solaris__) || defined(__osf__) || defined(__hpux__)) #include #endif #include #include char gTrackerFileTempDataPath[256]; void TestBCasterTracker(int x ) { BCasterTracker tracker( "trackerfile" ); if ( tracker.IsOpen() ) { if ( x > - 1 ) { int error; error = tracker.Remove( x ); if ( error ) // remove the xth item from the list. qtss_printf( "Playlist Broadcast (%li) not found.\n", (SInt32)x ); else tracker.Save(); } else { tracker.Show(); } } else qtss_printf("PlaylistBroadcaster trackerfile open FAILED.\n"); } static void ShowElement( PLDoubleLinkedListNode* ten, void* userData) { int* showIndex = (int*)userData; char *info; if ( BCasterTracker::IsProcessRunning( ten->fElement->mPID ) ) info = ""; else info = ", (not running)"; //qtss_printf( "[%li] %li %s%s\n", (SInt32)*showIndex, (SInt32)ten->fElement->mPID, ten->fElement->mName, info ); qtss_printf( "[%3li] %s; pid: %li%s\n", (SInt32)*showIndex, ten->fElement->mName, (SInt32)ten->fElement->mPID, info ); *(int*)userData = *showIndex + 1; } void BCasterTracker::Show() { int showIndex = 1; if ( mTrackingList.GetNumNodes() ) { qtss_printf( "\n" ); qtss_printf( "Current Playlist Broadcasts\n" ); qtss_printf( " ID Description file; Process ID\n" ); qtss_printf( "----------------------------------\n" ); // display the elements in the list mTrackingList.ForEach( ShowElement, &showIndex ); } else { qtss_printf( "\n" ); qtss_printf( "- PlaylistBroadcaster: No Broadcasts running.\n" ); } } bool BCasterTracker::IsProcessRunning( pid_t pid ) { bool isRunning=false; /* // Generic unix code char procPath[256]; qtss_sprintf( procPath, "ps -p%li | grep %li > %s ",(SInt32)pid,(SInt32)pid,gTrackerFileTempDataPath); int result = system(procPath); if (0 == result) { isRunning = true; } */ // a no-grep version to find the pid char pidStr[32]; qtss_sprintf( pidStr, "%li",(SInt32)pid); char procPath[64] = "ps -p"; ::strcat( procPath, pidStr); FILE *inPipe = ::popen(procPath, "r"); if (NULL == inPipe) return false; char inBuff[256] = ""; while (!isRunning && ::fgets(inBuff, sizeof(inBuff), inPipe) != 0) { if (::strstr(inBuff,pidStr) != NULL) { isRunning = true; break; } } (void) ::pclose(inPipe); return isRunning; } bool IsProcessID( PLDoubleLinkedListNode*ten, void* userData) { /* used by ForEachUntil to find a TrackingElement with a given Process ID userData is a pointer to the process id we want find in our list. */ pid_t pid; bool isProcID = false; pid = *(pid_t*)userData; if ( pid == ten->fElement->mPID ) isProcID = true; return isProcID; } int BCasterTracker::RemoveByProcessID( pid_t pid ) { int error = -1; // remove the element with the given process ID // from the tracking list. // called by the app when it is going away. // DO NOT kill the pid being passed in. PLDoubleLinkedListNode *te; te = mTrackingList.ForEachUntil( IsProcessID, &pid ); // remove that element if ( te ) { mTrackingList.RemoveNode(te); error = 0; } return error; } static bool IsElementID( PLDoubleLinkedListNode */*ten*/, void* userData) { /* used by ForEachUntil to find a TrackingElement with a given index number userData is a pointer to the counter, initialized to the index we want to find. we decrement it until is reaches zero. */ UInt32* showIndex = (UInt32*)userData; bool isItemId = false; // when the counter reduces to zero, return true. if ( *showIndex == 0 ) isItemId = true; else *(int*)userData = *showIndex - 1; return isItemId; } int BCasterTracker::Remove( UInt32 itemID ) { int error = -1; UInt32 itemIDIndex = itemID; // KILL the process that is associated with the item // id in our list. // remove the element with the given index in the list // from the tracking list. // itemID is zero based // set the index to the item number we want to find. Use ForEachUntil with IsElementID // to count down through the elements until we get to the Nth element. PLDoubleLinkedListNode *te; te = mTrackingList.ForEachUntil( IsElementID, &itemIDIndex ); // remove that element, and kill the process if ( te ) { if ( ::kill( te->fElement->mPID, SIGTERM ) == 0 ) { error = 0; } else { error = OSThread::GetErrno(); if ( error != ESRCH ) // no such process { // we probably cannot kill this process because it is not ours } else { // this process already died, or was killed by other means. error = 0; } } if ( !error ) { mTrackingList.RemoveNode(te); } } return error; } int BCasterTracker::Add( pid_t pid, const char* bcastList ) { /* add an entry to our tracking list */ int addErr = -1; TrackingElement* te = new TrackingElement( pid, bcastList ); Assert( te ); if ( te ) { PLDoubleLinkedListNode* ten = new PLDoubleLinkedListNode( te ); Assert( ten ); if ( ten ) { mTrackingList.AddNodeToTail( ten ); addErr = 0; } else delete te; } return addErr; } BCasterTracker::~BCasterTracker() { // truncate the file to the desired size and close. if ( mTrackerFile != NULL ) { int err = OSThread::GetErrno(); if ( ::fseek( mTrackerFile, mEofPos, SEEK_SET ) < 0 ) qtss_printf( "fseek at close eof(%li), err(%li), fileno(%li)\n", mEofPos, (SInt32)err, (SInt32)fileno(mTrackerFile) ); if ( ::fflush( mTrackerFile ) ) qtss_printf( "fflush at close eof(%li), err(%li), fileno(%li)\n", mEofPos, (SInt32)err, (SInt32)fileno(mTrackerFile) ); if ( ::ftruncate( fileno(mTrackerFile), (off_t)mEofPos ) ) { qtss_printf( "ftruncate at close eof(%li), err(%li), fileno(%li)\n", mEofPos, (SInt32)err, (SInt32)fileno(mTrackerFile) ); } (void)::fclose( mTrackerFile ); } } static bool SaveElement( PLDoubleLinkedListNode* ten, void* userData) { FILE* fp = (FILE*) userData; char buff[512]; /* used by ForEachUntil to find a SaveElement each element to the tracking file. userData is a pointer to the FILE of our open tracking file. */ qtss_sprintf( buff, "%li \"%s\"\n", (SInt32)ten->fElement->mPID, ten->fElement->mName ); // linux version of fputs returns <0 for err, or num bytes written // mac os X version of fputs returns <0 for err, or 0 for no err if (::fputs( buff, fp ) < 0 ) return true; return false; } int BCasterTracker::Save() { /* save each record in the the tracker to disk. return 0 on success, < 0 otherwise. */ int error = -1; mEofPos = 0; if ( mTrackerFile != NULL ) { if ( !::fseek( mTrackerFile, 0, SEEK_SET ) ) { if ( !mTrackingList.ForEachUntil( SaveElement, mTrackerFile ) ) { mEofPos = ::ftell( mTrackerFile ); error = 0; } } } return error; } bool BCasterTracker::IsOpen() { // return false if the file is not open, // true if the file is open. if ( mTrackerFile == NULL ) return false; else return true; } BCasterTracker::BCasterTracker( const char* name ) { mEofPos = 0; mTrackerFile = NULL; time_t calendarTime = 0; calendarTime = ::time(NULL) + 10; // wait a SInt32 time for access to the file. // 2 possible loops one to try to open ( and possible create ) the file // the second to obtain an exclusive lock on the file. // the app should probably fail if this cannot be done within the alloted time //qtss_printf("time=%"_S32BITARG_"\n",calendarTime); while ( mTrackerFile == NULL && calendarTime > ::time(NULL) ) { mTrackerFile = ::fopen( name, "r+" ); if ( !mTrackerFile ) { // try and create mTrackerFile = ::fopen( name, "a+" ); if ( mTrackerFile ) { // let "everyone" read and write this file so that we can track // all the broadcasters no matter which user starts them (void)::chmod( name, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH ); (void)::fclose( mTrackerFile ); } mTrackerFile = NULL; } ::sleep(1); } ::strcpy(gTrackerFileTempDataPath,name); ::strcat( gTrackerFileTempDataPath, "_tmp" ); FILE * tempFile = NULL; calendarTime = ::time(NULL) + 10; while ( tempFile == NULL && calendarTime > ::time(NULL) ) { tempFile = ::fopen( gTrackerFileTempDataPath, "r+" ); if ( !tempFile ) { // try and create tempFile = ::fopen( gTrackerFileTempDataPath, "a+" ); if ( tempFile ) { // let "everyone" read and write this file so that we can track // all the broadcasters no matter which user starts them (void)::chmod( gTrackerFileTempDataPath, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH ); } } } if (tempFile) (void)::fclose( tempFile ); if ( mTrackerFile ) { bool haveLock = false; while ( !haveLock && calendarTime > ::time(NULL) ) { struct flock lock_params; lock_params.l_start = 0; /* starting offset */ lock_params.l_len = 0; /* len = 0 means until end of file */ lock_params.l_pid = getpid(); /* lock owner */ lock_params.l_type = F_WRLCK; /* lock type: read/write, etc. */ lock_params.l_whence = 0; /* type of l_start */ if ( !::fcntl( fileno(mTrackerFile), F_SETLK, &lock_params ) ) haveLock = true; } // if can't lock it, close it. if ( !haveLock ) { (void)::fclose( mTrackerFile ); mTrackerFile = NULL; } } if ( mTrackerFile ) { SInt32 lineBuffSize = kTrackerLineBuffSize; SInt32 wordBuffSize = kTrackerLineBuffSize; char lineBuff[kTrackerLineBuffSize]; char wordBuff[kTrackerLineBuffSize]; char *next; UInt32 pid; int error = 0; error = ::fseek( mTrackerFile, 0, SEEK_SET ); Assert(error == 0); do { // get a line ( fgets adds \n+ 0x00 ) if ( ::fgets( lineBuff, lineBuffSize, mTrackerFile ) == NULL ) break; // trim the leading whitespace next = ::TrimLeft( lineBuff ); if (*next) { // grab the pid next = ::GetWord( wordBuff, next, wordBuffSize ); Assert( *wordBuff ); pid = ::atoi( wordBuff ); if (*next) next = ::TrimLeft( next ); // grab the broadcast list string if (*next) { next = ::TrimLeft( next ); if (*next == '"' ) next = ::GetQuotedWord( wordBuff, next, wordBuffSize ); else next = ::GetWord( wordBuff, next, wordBuffSize ); if ( this->IsProcessRunning( pid ) ) { if (*wordBuff) { error = this->Add( pid, wordBuff ); } } } } } while ( feof( mTrackerFile ) == 0 && error == 0 ); mEofPos = ::ftell( mTrackerFile ); } // dtor closes file... }