579 lines
15 KiB
C++
579 lines
15 KiB
C++
|
/*
|
||
|
*
|
||
|
* @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 <stdlib.h>
|
||
|
#include "SafeStdLib.h"
|
||
|
#include <stdio.h>
|
||
|
#include <errno.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <sys/file.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <sys/types.h>
|
||
|
#if !(defined(__solaris__) || defined(__osf__) || defined(__hpux__))
|
||
|
#include <sys/sysctl.h>
|
||
|
#endif
|
||
|
#include <sys/time.h>
|
||
|
#include <signal.h>
|
||
|
|
||
|
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<TrackingElement>* 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<TrackingElement>*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<TrackingElement> *te;
|
||
|
|
||
|
te = mTrackingList.ForEachUntil( IsProcessID, &pid );
|
||
|
|
||
|
// remove that element
|
||
|
if ( te )
|
||
|
{
|
||
|
mTrackingList.RemoveNode(te);
|
||
|
error = 0;
|
||
|
}
|
||
|
|
||
|
return error;
|
||
|
|
||
|
}
|
||
|
|
||
|
static bool IsElementID( PLDoubleLinkedListNode<TrackingElement> */*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<TrackingElement> *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<TrackingElement>* ten = new PLDoubleLinkedListNode<TrackingElement>( 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<TrackingElement>* 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...
|
||
|
|
||
|
}
|