~ubuntu-branches/ubuntu/trusty/gnustep-base/trusty

« back to all changes in this revision

Viewing changes to Source/NSTask.m

Tags: upstream-1.20.0
ImportĀ upstreamĀ versionĀ 1.20.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
   Boston, MA 02111 USA.
23
23
 
24
24
   <title>NSTask class reference</title>
25
 
   $Date: 2008-11-17 13:45:32 +0000 (Mon, 17 Nov 2008) $ $Revision: 27080 $
 
25
   $Date: 2010-04-23 10:05:55 -0600 (Fri, 23 Apr 2010) $ $Revision: 30224 $
26
26
   */
27
27
 
28
 
#include "config.h"
29
 
#include "GNUstepBase/preface.h"
30
 
#include "Foundation/NSObject.h"
31
 
#include "Foundation/NSAutoreleasePool.h"
32
 
#include "Foundation/NSBundle.h"
33
 
#include "Foundation/NSCharacterSet.h"
34
 
#include "Foundation/NSData.h"
35
 
#include "Foundation/NSDate.h"
36
 
#include "Foundation/NSEnumerator.h"
37
 
#include "Foundation/NSString.h"
38
 
#include "Foundation/NSException.h"
39
 
#include "Foundation/NSFileHandle.h"
40
 
#include "Foundation/NSFileManager.h"
41
 
#include "Foundation/NSMapTable.h"
42
 
#include "Foundation/NSProcessInfo.h"
43
 
#include "Foundation/NSRunLoop.h"
44
 
#include "Foundation/NSNotification.h"
45
 
#include "Foundation/NSNotificationQueue.h"
46
 
#include "Foundation/NSTask.h"
47
 
#include "Foundation/NSTimer.h"
48
 
#include "Foundation/NSLock.h"
49
 
#include "Foundation/NSDebug.h"
50
 
#include "GSPrivate.h"
 
28
#import "common.h"
 
29
#define EXPOSE_NSTask_IVARS     1
 
30
#import "Foundation/NSAutoreleasePool.h"
 
31
#import "Foundation/NSBundle.h"
 
32
#import "Foundation/NSCharacterSet.h"
 
33
#import "Foundation/NSData.h"
 
34
#import "Foundation/NSDate.h"
 
35
#import "Foundation/NSEnumerator.h"
 
36
#import "Foundation/NSException.h"
 
37
#import "Foundation/NSFileHandle.h"
 
38
#import "Foundation/NSFileManager.h"
 
39
#import "Foundation/NSMapTable.h"
 
40
#import "Foundation/NSProcessInfo.h"
 
41
#import "Foundation/NSRunLoop.h"
 
42
#import "Foundation/NSNotification.h"
 
43
#import "Foundation/NSNotificationQueue.h"
 
44
#import "Foundation/NSTask.h"
 
45
#import "Foundation/NSTimer.h"
 
46
#import "Foundation/NSLock.h"
 
47
#import "GNUstepBase/NSString+GNUstepBase.h"
 
48
#import "GNUstepBase/NSObject+GNUstepBase.h"
 
49
#import "GSPrivate.h"
51
50
 
52
51
#include <string.h>
53
52
#ifdef HAVE_UNISTD_H
84
83
#include <sys/param.h>
85
84
#endif
86
85
 
 
86
 
87
87
/*
88
88
 *      If we are on a streams based system, we need to include stropts.h
89
89
 *      for definitions needed to set up slave pseudo-terminal stream.
103
103
#define NOFILE  256
104
104
#endif
105
105
 
 
106
 
106
107
@interface      NSBundle(Private)
107
108
+ (NSString *) _absolutePathOfExecutable: (NSString *)path;
108
109
+ (NSString*) _gnustep_target_cpu;
118
119
static void handleSignal(int sig)
119
120
{
120
121
  hadChildSignal = YES;
121
 
#ifndef __MINGW32__
 
122
#ifndef __MINGW__
122
123
  signal(SIGCHLD, handleSignal);
123
124
#endif
124
125
}
125
126
 
126
 
#ifdef __MINGW32__
 
127
#ifdef __MINGW__
127
128
@interface NSConcreteWindowsTask : NSTask
128
129
{
129
130
@public
263
264
        }
264
265
      [gnustep_global_lock unlock];
265
266
 
266
 
#ifndef __MINGW32__
 
267
#ifndef __MINGW__
267
268
      signal(SIGCHLD, handleSignal);
268
269
#endif
269
270
    }
285
286
  return AUTORELEASE(task);
286
287
}
287
288
 
288
 
- (void) gcFinalize
 
289
- (void) finalize
289
290
{
290
291
  [tasksLock lock];
291
292
  NSMapRemove(activeTasks, (void*)(intptr_t)_taskId);
294
295
 
295
296
- (void) dealloc
296
297
{
297
 
  [self gcFinalize];
 
298
  [self finalize];
298
299
  RELEASE(_arguments);
299
300
  RELEASE(_environment);
300
301
  RELEASE(_launchPath);
358
359
      return;
359
360
    }
360
361
 
361
 
#ifndef __MINGW32__
 
362
#ifndef __MINGW__
362
363
#ifdef  HAVE_KILLPG
363
364
  killpg(_taskId, SIGINT);
364
365
#else
426
427
      [NSException raise: NSInvalidArgumentException
427
428
                  format: @"NSTask - task has not yet launched"];
428
429
    }
429
 
#ifndef __MINGW32__
 
430
#ifndef __MINGW__
430
431
#ifdef  HAVE_KILLPG
431
432
  killpg(_taskId, SIGCONT);
432
433
#else
617
618
      [NSException raise: NSInvalidArgumentException
618
619
                  format: @"NSTask - task has not yet launched"];
619
620
    }
620
 
#ifndef __MINGW32__
 
621
#ifndef __MINGW__
621
622
#ifdef  HAVE_KILLPG
622
623
  killpg(_taskId, SIGSTOP);
623
624
#else
649
650
    }
650
651
 
651
652
  _hasTerminated = YES;
652
 
#ifndef __MINGW32__
 
653
#ifndef __MINGW__
653
654
#ifdef  HAVE_KILLPG
654
655
  killpg(_taskId, SIGTERM);
655
656
#else
750
751
  full_path = [arch_path stringByAppendingPathComponent: libs];
751
752
 
752
753
  lpath = [full_path stringByAppendingPathComponent: prog];
753
 
#ifdef  __MINGW32__
 
754
#ifdef  __MINGW__
754
755
  if ([mgr isExecutableFileAtPath: lpath] == NO
755
756
    && [mgr isExecutableFileAtPath:
756
757
    (lpath = [lpath stringByAppendingPathExtension: @"exe"])] == NO)
759
760
#endif
760
761
    {
761
762
      lpath = [arch_path stringByAppendingPathComponent: prog];
762
 
#ifdef  __MINGW32__
 
763
#ifdef  __MINGW__
763
764
      if ([mgr isExecutableFileAtPath: lpath] == NO
764
765
        && [mgr isExecutableFileAtPath:
765
766
        (lpath = [lpath stringByAppendingPathExtension: @"exe"])] == NO)
768
769
#endif
769
770
        {
770
771
          lpath = [base_path stringByAppendingPathComponent: prog];
771
 
#ifdef  __MINGW32__
 
772
#ifdef  __MINGW__
772
773
          if ([mgr isExecutableFileAtPath: lpath] == NO
773
774
            && [mgr isExecutableFileAtPath:
774
775
            (lpath = [lpath stringByAppendingPathExtension: @"exe"])] == NO)
787
788
                }
788
789
              if (lpath != nil)
789
790
                {
790
 
#ifdef  __MINGW32__
 
791
#ifdef  __MINGW__
791
792
                  if ([mgr isExecutableFileAtPath: lpath] == NO
792
793
                    && [mgr isExecutableFileAtPath:
793
794
                    (lpath = [lpath stringByAppendingPathExtension: @"exe"])]
815
816
        }
816
817
      lpath = [lpath stringByStandardizingPath];
817
818
    }
818
 
#ifdef  __MINGW32__
 
819
#ifdef  __MINGW__
819
820
  /** We need this to be native windows format, and some of the standardisation
820
821
   * above may have left unix style separators in the string.
821
822
   */
918
919
 
919
920
@end
920
921
 
921
 
#ifdef __MINGW32__
 
922
#ifdef __MINGW__
922
923
@implementation NSConcreteWindowsTask
923
924
 
924
925
BOOL
954
955
  return found;
955
956
}
956
957
 
957
 
- (void) gcFinalize
 
958
- (void) finalize
958
959
{
959
 
  [super gcFinalize];
 
960
  [super finalize];
960
961
  if (wThread != NULL)
961
962
    {
962
963
      CloseHandle(wThread);
1092
1093
  NSDictionary          *env;
1093
1094
  NSMutableArray        *toClose;
1094
1095
  NSFileHandle          *hdl;
 
1096
  HANDLE                hIn;
 
1097
  HANDLE                hOut;
 
1098
  HANDLE                hErr;
 
1099
  id                    last = nil;
1095
1100
 
1096
1101
  if (_hasLaunched)
1097
1102
    {
1163
1168
  start_info.dwFlags |= STARTF_USESTDHANDLES;
1164
1169
 
1165
1170
  toClose = [NSMutableArray arrayWithCapacity: 3];
1166
 
  hdl = [self standardInput];
1167
 
  if ([hdl isKindOfClass: [NSPipe class]])
1168
 
    {
1169
 
      hdl = [(NSPipe*)hdl fileHandleForReading];
1170
 
      [toClose addObject: hdl];
1171
 
    }
1172
 
  start_info.hStdInput = [hdl nativeHandle];
1173
 
 
1174
 
  hdl = [self standardOutput];
1175
 
  if ([hdl isKindOfClass: [NSPipe class]])
1176
 
    {
1177
 
      hdl = [(NSPipe*)hdl fileHandleForWriting];
1178
 
      [toClose addObject: hdl];
1179
 
    }
1180
 
  start_info.hStdOutput = [hdl nativeHandle];
1181
 
 
1182
 
  hdl = [self standardError];
1183
 
  if ([hdl isKindOfClass: [NSPipe class]])
1184
 
    {
1185
 
      hdl = [(NSPipe*)hdl fileHandleForWriting];
1186
 
      /*
1187
 
       * If we have the same pipe twice we don't want to close it twice
1188
 
       */
1189
 
      if ([toClose indexOfObjectIdenticalTo: hdl] == NSNotFound)
1190
 
        {
1191
 
          [toClose addObject: hdl];
1192
 
        }
1193
 
    }
1194
 
  start_info.hStdError = [hdl nativeHandle];
 
1171
 
 
1172
  if (_standardInput == nil)
 
1173
    {
 
1174
      start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
 
1175
    }
 
1176
  else
 
1177
    {
 
1178
      hdl = [self standardInput];
 
1179
      if ([hdl isKindOfClass: [NSPipe class]])
 
1180
        {
 
1181
          hdl = [(NSPipe*)hdl fileHandleForReading];
 
1182
          [toClose addObject: hdl];
 
1183
        }
 
1184
      start_info.hStdInput = [hdl nativeHandle];
 
1185
    }
 
1186
  hIn = start_info.hStdInput;
 
1187
 
 
1188
  if (_standardOutput == nil)
 
1189
    {
 
1190
      start_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
 
1191
    }
 
1192
  else
 
1193
    {
 
1194
      hdl = [self standardOutput];
 
1195
      if ([hdl isKindOfClass: [NSPipe class]])
 
1196
        {
 
1197
          hdl = [(NSPipe*)hdl fileHandleForWriting];
 
1198
          [toClose addObject: hdl];
 
1199
        }
 
1200
      start_info.hStdOutput = [hdl nativeHandle];
 
1201
    }
 
1202
  hOut = start_info.hStdOutput;
 
1203
 
 
1204
  if (_standardError == nil)
 
1205
    {
 
1206
      start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
 
1207
    }
 
1208
  else
 
1209
    {
 
1210
      hdl = [self standardError];
 
1211
      if ([hdl isKindOfClass: [NSPipe class]])
 
1212
        {
 
1213
          hdl = [(NSPipe*)hdl fileHandleForWriting];
 
1214
          /*
 
1215
           * If we have the same pipe twice we don't want to close it twice
 
1216
           */
 
1217
          if ([toClose indexOfObjectIdenticalTo: hdl] == NSNotFound)
 
1218
            {
 
1219
              [toClose addObject: hdl];
 
1220
            }
 
1221
        }
 
1222
      start_info.hStdError = [hdl nativeHandle];
 
1223
    }
 
1224
  hErr = start_info.hStdError;
 
1225
 
 
1226
  /* Tell the system not to show a window for the subtask.
 
1227
   */
 
1228
  start_info.wShowWindow = SW_HIDE;
 
1229
  start_info.dwFlags |= STARTF_USESHOWWINDOW;
 
1230
 
 
1231
  /* Make the handles inheritable only temporarily while launching the
 
1232
   * child task.  This section must be lock protected so we don't have
 
1233
   * another thread trying to launch at the same time and get handles
 
1234
   * inherited by the wrong threads.
 
1235
   */
 
1236
  [tasksLock lock];
 
1237
  SetHandleInformation(hIn, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
 
1238
  SetHandleInformation(hOut, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
 
1239
  SetHandleInformation(hErr, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
1195
1240
 
1196
1241
  result = CreateProcessW(wexecutable,
1197
1242
    w_args,
1198
1243
    NULL,                       /* proc attrs */
1199
1244
    NULL,                       /* thread attrs */
1200
1245
    1,                          /* inherit handles */
1201
 
//    CREATE_NO_WINDOW|DETACHED_PROCESS|CREATE_UNICODE_ENVIRONMENT,
1202
 
    CREATE_NO_WINDOW|CREATE_UNICODE_ENVIRONMENT,
 
1246
    0
 
1247
    |CREATE_NO_WINDOW
 
1248
/* One would have thought the the CREATE_NO_WINDOW flag should be used,
 
1249
 * but apparently this breaks for old 16bit applications/tools on XP.
 
1250
 * So maybe we want to leave it out?
 
1251
 */
 
1252
//    |DETACHED_PROCESS
 
1253
/* We don't set the DETACHED_PROCESS flag as it actually means that the
 
1254
 * child task should get a new Console allocated ... and that means it
 
1255
 * will pop up a console window ... which looks really bad.
 
1256
 */
 
1257
    |CREATE_UNICODE_ENVIRONMENT,
1203
1258
    envp,                       /* env block */
1204
1259
    (const unichar*)[[self currentDirectoryPath] fileSystemRepresentation],
1205
1260
    &start_info,
1206
1261
    &procInfo);
 
1262
  if (result == 0)
 
1263
    {
 
1264
      last = [NSError _last];
 
1265
    }
1207
1266
  NSZoneFree(NSDefaultMallocZone(), w_args);
 
1267
  SetHandleInformation(hIn, HANDLE_FLAG_INHERIT, 0);
 
1268
  SetHandleInformation(hOut, HANDLE_FLAG_INHERIT, 0);
 
1269
  SetHandleInformation(hErr, HANDLE_FLAG_INHERIT, 0);
 
1270
  [tasksLock unlock];
 
1271
 
1208
1272
  if (result == 0)
1209
1273
    {
1210
 
      NSLog(@"Error launching task: %@", lpath);
 
1274
      NSLog(@"Error launching task: %@ ... %@", lpath, last);
1211
1275
      return;
1212
1276
    }
1213
1277
 
1271
1335
 
1272
1336
      do
1273
1337
        {
 
1338
          NSTask        *t;
 
1339
 
 
1340
          errno = 0;
1274
1341
          result = waitpid(-1, &status, WNOHANG);
1275
 
          if (result > 0)
1276
 
            {
1277
 
              NSTask    *t;
1278
 
 
1279
 
              [tasksLock lock];
1280
 
              t = (NSTask*)NSMapGet(activeTasks, (void*)(intptr_t)result);
1281
 
              AUTORELEASE(RETAIN(t));
 
1342
          if (result < 0)
 
1343
            {
 
1344
#if     defined(WAITDEBUG)
 
1345
              [tasksLock lock];
 
1346
              t = (NSTask*)NSMapGet(activeTasks, (void*)(intptr_t)result);
 
1347
              [tasksLock unlock];
 
1348
              if (t != nil)
 
1349
                {
 
1350
                  NSLog(@"waitpid result %d, error %@",
 
1351
                    result, [NSError _last]);
 
1352
                }
 
1353
#endif
 
1354
            }
 
1355
          else if (result > 0)
 
1356
            {
 
1357
              [tasksLock lock];
 
1358
              t = (NSTask*)NSMapGet(activeTasks, (void*)(intptr_t)result);
 
1359
              IF_NO_GC(AUTORELEASE(RETAIN(t));)
1282
1360
              [tasksLock unlock];
1283
1361
              if (t != nil)
1284
1362
                {
1285
1363
                  if (WIFEXITED(status))
1286
1364
                    {
 
1365
#if     defined(WAITDEBUG)
 
1366
                      NSLog(@"waitpid %d, exit status = %d",
 
1367
                        result, status);
 
1368
#endif
1287
1369
                      [t _terminatedChild: WEXITSTATUS(status)];
1288
1370
                      found = YES;
1289
1371
                    }
1290
1372
                  else if (WIFSIGNALED(status))
1291
1373
                    {
 
1374
#if     defined(WAITDEBUG)
 
1375
                      NSLog(@"waitpid %d, termination status = %d",
 
1376
                        result, status);
 
1377
#endif
1292
1378
                      [t _terminatedChild: WTERMSIG(status)];
1293
1379
                      found = YES;
1294
1380
                    }
1298
1384
                        result);
1299
1385
                    }
1300
1386
                }
 
1387
#if     defined(WAITDEBUG)
 
1388
              else
 
1389
                {
 
1390
                  NSLog(@"Received signal for unknown child %d", result);
 
1391
                }
 
1392
#endif
1301
1393
            }
1302
1394
        }
1303
1395
      while (result > 0);
1392
1484
    }
1393
1485
  edesc = [hdl fileDescriptor];
1394
1486
 
1395
 
  pid = fork();
 
1487
  pid = vfork();
1396
1488
  if (pid < 0)
1397
1489
    {
1398
1490
      [NSException raise: NSInvalidArgumentException
1402
1494
    {
1403
1495
      int       i;
1404
1496
 
1405
 
      /*
1406
 
       * Make sure the task gets default signal setup.
 
1497
      /* Make sure the task gets default signal setup.
1407
1498
       */
1408
1499
      for (i = 0; i < 32; i++)
1409
1500
        {
1410
1501
          signal(i, SIG_DFL);
1411
1502
        }
1412
1503
 
1413
 
      /*
1414
 
       * Make sure task is run in it's own process group.
 
1504
      /* Make sure task is session leader in it's own process group
 
1505
       * and with no controlling terminal.
 
1506
       * This allows us to use killpg() to put the task to sleep etc,
 
1507
       * and have the signal effect forked children of the subtask.
1415
1508
       */
1416
 
#ifdef     HAVE_SETPGRP
1417
 
#ifdef  SETPGRP_VOID
 
1509
#if     defined(HAVE_SETSID)
 
1510
      setsid();
 
1511
#else
 
1512
#if     defined(HAVE_SETPGRP)
 
1513
#if     defined(SETPGRP_VOID)
1418
1514
      setpgrp();
1419
1515
#else
1420
1516
      setpgrp(getpid(), getpid());
1421
1517
#endif
1422
1518
#else
1423
 
#if defined(__MINGW32__)
 
1519
#if     defined(HAVE_SETPGID)
 
1520
#if defined(__MINGW__)
1424
1521
      pid = (int)GetCurrentProcessId(),
1425
1522
#else
1426
1523
      pid = (int)getpid();
1427
1524
#endif
1428
 
#ifdef     HAVE_SETPGID
1429
1525
      setpgid(pid, pid);
1430
 
#endif
1431
 
#endif
 
1526
#endif  /* HAVE_SETPGID */
 
1527
#endif  /* HAVE_SETPGRP */
 
1528
      /* Detach from controlling terminal.
 
1529
       */
 
1530
#if     defined(TIOCNOTTY)
 
1531
      i = open("/dev/tty", O_RDWR);
 
1532
      if (i >= 0)
 
1533
        {
 
1534
          (void)ioctl(i, TIOCNOTTY, 0);
 
1535
          (void)close(i);
 
1536
        }
 
1537
#endif  /* TIOCNOTTY */
 
1538
#endif  /* HAVE_SETSID */
1432
1539
 
1433
1540
      if (_usePseudoTerminal == YES)
1434
1541
        {
1440
1547
              exit(1);                  /* Failed to open slave!        */
1441
1548
            }
1442
1549
 
1443
 
#ifdef  HAVE_SETSID
1444
 
          i = setsid();
1445
 
#endif
1446
 
#ifdef  TIOCNOTTY
1447
 
          i = open("/dev/tty", O_RDWR);
1448
 
          if (i >= 0)
1449
 
            {
1450
 
              (void)ioctl(i, TIOCNOTTY, 0);
1451
 
              (void)close(i);
1452
 
            }
1453
 
#endif
1454
 
          /*
1455
 
           * Set up stdin, stdout and stderr by duplicating descriptors as
 
1550
          /* Set up stdin, stdout and stderr by duplicating descriptors as
1456
1551
           * necessary and closing the originals (to ensure we won't have a
1457
1552
           * pipe left with two write descriptors etc).
1458
1553
           */
1471
1566
        }
1472
1567
      else
1473
1568
        {
1474
 
          /*
1475
 
           * Set up stdin, stdout and stderr by duplicating descriptors as
 
1569
          /* Set up stdin, stdout and stderr by duplicating descriptors as
1476
1570
           * necessary and closing the originals (to ensure we won't have a
1477
1571
           * pipe left with two write descriptors etc).
1478
1572
           */
1556
1650
 
1557
1651
- (void) _collectChild
1558
1652
{
1559
 
  if (_hasCollected == NO)
1560
 
    {
1561
 
      int       result;
1562
 
 
1563
 
      errno = 0;
1564
 
      result = waitpid(_taskId, &_terminationStatus, WNOHANG);
1565
 
      if (result < 0)
1566
 
        {
1567
 
          NSLog(@"waitpid %d, result %d, error %@",
1568
 
            _taskId, result, [NSError _last]);
1569
 
          [self _terminatedChild: -1];
1570
 
        }
1571
 
      else if (result == _taskId || (result > 0 && errno == 0))
1572
 
        {
1573
 
          if (WIFEXITED(_terminationStatus))
1574
 
            {
1575
 
#ifdef  WAITDEBUG
1576
 
              NSLog(@"waitpid %d, termination status = %d",
1577
 
                _taskId, _terminationStatus);
1578
 
#endif
1579
 
              [self _terminatedChild: WEXITSTATUS(_terminationStatus)];
1580
 
            }
1581
 
          else if (WIFSIGNALED(_terminationStatus))
1582
 
            {
1583
 
#ifdef  WAITDEBUG
1584
 
              NSLog(@"waitpid %d, termination status = %d",
1585
 
                _taskId, _terminationStatus);
1586
 
#endif
1587
 
              [self _terminatedChild: WTERMSIG(_terminationStatus)];
1588
 
            }
1589
 
#ifdef  WAITDEBUG
1590
 
          else
1591
 
            {
1592
 
              NSLog(@"waitpid %d, event status = %d",
1593
 
                _taskId, _terminationStatus);
1594
 
            }
1595
 
#endif
1596
 
        }
1597
 
#ifdef  WAITDEBUG
1598
 
      else
1599
 
        {
1600
 
          NSLog(@"waitpid %d, result %d, error %@",
1601
 
            _taskId, result, [NSError _last]);
1602
 
        }
1603
 
#endif
1604
 
    }
 
1653
  GSPrivateCheckTasks();
1605
1654
}
1606
1655
 
1607
1656
- (BOOL) usePseudoTerminal