Разработка приложений в среде Linux. Второе издание

Джонсон Майкл К.

Троан Эрик В.

Приложения

 

 

Приложение A

Заголовочные файлы

В этом приложении показаны все локальные заголовочные файлы для исходного кода, рассмотренного в книге.

1: /* libhello.h */

2:

3: #ifndef LIBHELLO_H_

4: #define LIBHELLO_H_

5:

6: void print_hello(void);

7:

8: #endif /* LIBHELLO_H_ */

1: /* ptypair.h */

2:

3: #ifndef _PTYPAIR_H

4: #define _PTYPAIR_H

5: int get_master_pty(char **name);

6: int get_slave_pty(char *name);

7: #endif /* _PTYPAIR_H */

1: /* sockutil.h */

2:

3: void die(char * message);

4: void copyData(int from, int to);

5: #ifndef CMSG_DATA

6: #define CMSG_DATA (cmsg) ((cmsg)->cmsg_data)

7: #endif

 

Приложение Б

Исходный код

ladsh

  1: /* ladsh4.c */

  2:

  3: #define _GNU_SOURCE

  4:

  5: #include

  6: #include

  7: #include

  8: #include

  9: #include

 10: #include

 11: #include

 12: #include

 13: #include

 14: #include

 15: #include

 16:

 17: #define MAX_COMMAND_LEN 250 /* максимальная длина одной

 18:                                командной строки */

 19: #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"

 20:

 21: struct jobSet {

 22:  struct job * head; /* заголовок списка выполняющихся заданий */

 23:  struct job * fg; /* текущее высокоприоритетное задание */

 24: };

 25:

 26: enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,

 27:                        REDIRECT_APPEND };

 28:

 29: struct redirectionSpecifier {

 30:  enum redirectionType type; /* тип переадресации */

 31:  int fd;                    /* переадресация fd */

 32:  char * filename;           /* файл, в который будет переадресовано fd */

 33: };

 34:

 35: struct childProgram {

 36:  pid_t pid;                                 /* 0 в случае выхода */

 37:  char ** argv;                              /* имя программы и аргументы */

 38:  int numRedirections;                       /* элементы в массиве переадресации */

 39:  struct redirectionSpecifier* redirections; /* переадресации ввода-вывода */

 40:  glob_t globResult;                         /* результат универсализации параметра */

 41:  int freeGlob;                              /* нужно ли освобождать globResult? */

 42:  int isStopped;                             /* выполняется ли в данный момент программа?*/

 43: };

 44:

 45: struct job {

 46:  int jobId;        /* номер задания */

 47:  int numProgs;     /* количество программ в задании */

 48:  int runningProgs; /* количество выполняющихся программ */

 49:  char * text;      /* имя задания */

 50:  char * cmdBuf;    /* буфер, на который ссылаются различные массивы argv */

 51:  pid_t pgrp;       /* идентификатор группы процесса для задания */

 52:  struct childProgram* progs; /* массив программ в задании */

 53:  struct job* next; /* для отслеживания фоновых команд */

 54:  int stoppedProgs; /* количество активных, но приостановленных программ */

 55: };

 56:

 57: void freeJob (struct job * cmd) {

 58:  int i;

 59:

 60:  for (i = 0; i numProgs; i++) {

 61:   free(cmd->progs[i].argv);

 62:   if (cmd->progs[i].redirections)

 63:    free(cmd->progs[i].redirections);

 64:   if (cmd->progs[i].freeGlob)

 65:    globfree(&cmd->progs[i].globResult);

 66:  }

 67:  free(cmd->progs);

 68:  if (cmd->text) free(cmd->text);

 69:   free(cmd->cmdBuf);

 70: }

 71:

 72: int getCommand(FILE * source, char * command) {

 73:  if (source == stdin) {

 74:   printf("# ");

 75:   fflush(stdout);

 76:  }

 77:

 78:  if (!fgets(command, MAX_COMMAND_LEN, source)) {

 79:   if (source == stdin) printf("\n");

 80:   return 1;

 81:  }

 82:

 83:  /* удаление хвостового символа новой строки */

 84:  command[strlen(command) - 1] = '\0';

 85:

 86:  return 0;

 87: }

 88:

 89: void globLastArgument(struct childProgram * prog, int * argcPtr,

 90:  int * argcAllocedPtr) {

 91:  int argc = *argcPtr;

 92:  int argcAlloced = *argcAllocedPtr;

 93:  int rc;

 94:  int flags;

 95:  int i;

 96:  char * src, * dst;

 98:  if (argc>1) { /* cmd->globResult уже инициализирован */

 99:   flags = GLOB_APPEND;

100:   i = prog->globResult.gl_pathc;

101:  } else {

102:   prog->freeGlob = 1;

103:   flags = 0;

104:   i = 0;

105:  }

106:

107:  rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);

108:  if (rc == GLOB_NOSPACE) {

109:   fprintf(stderr, "недостаточно пространства для универсализации\n");

110:   return;

111:  } else if (rc == GLOB_NOMATCH ||

112:   (!rc && (prog->globResult.gl_pathc - i) == 1 &&

113:   !strcmp(prog->argv[argc - 1],

114:   prog->globResult.gl_pathv[i]))) {

115:   /* нам нужно удалить все, что до сих пор было заключено между \ */

116:   src = dst = prog->argv[argc - 1];

117:   while (*src) {

118:    if (*src != '\\') *dst++ = *src;

119:    src++;

120:   }

121:   *dst = '\0';

122:  } else if (!rc) {

123:   argcAlloced += (prog->globResult.gl_pathc - i);

124:   prog->argv = realloc(prog->argv,

125:   argcAlloced * sizeof(*prog->argv));

126:   memcpy(prog->argv + (argc - 1),

127:   prog->globResult.gl_pathv + i,

128:   sizeof(*(prog->argv)) *

129:    (prog->globResult.gl_pathc - i));

130:   argc += (prog->globResult.gl_pathc - i - 1);

131:  }

132:

133:  *argcAllocedPtr = argcAlloced;

134:  *argcPtr = argc;

135: }

136:

137: /* Возвращаем cmd->numProgs как 0, если не представлено ни одной команды

138:    (например, пустая строка). Если будет обнаружена допустимая команда,

139:    commandPtr будет ссылаться на начало следующей команды (если исходная

140:    команда была связана с несколькими заданиями) или будет равно NULL,

141:    если больше не представлено ни одной команды. */

142: int parseCommand(char ** commandPtr, struct job * job, int * isBg) {

143:  char * command;

144:  char * returnCommand = NULL;

145:  char * src, * buf, * chptr;

146:  int argc = 0;

147:  int done = 0;

148:  int argvAlloced;

149:  int i;

150:  char quote = '\0';

151:  int count;

152:  struct childProgram * prog;

153:

154:  /* пропускаем первое свободное место (например, пробел) */

155:  while (**commandPtr && isspace(**commandPtr)) (*commandPtr)++;

156:

157:  /* обрабатываем пустые строки и первые символы '#' */

158:  if (!**commandPtr || (**commandPtr=='#')) {

159:   job->numProgs = 0;

160:   *commandPtr = NULL;

161:   return 0;

162:  }

163:

164:  *isBg = 0;

165:  job->numProgs = 1;

166:  job->progs = malloc(sizeof(*job->progs));

167:

168:  /* Мы задаем элементы массива argv для ссылки внутри строки.

169:     Освобождение памяти осуществляется с помощью функции freeJob().

170:

171:     Получив незанятую память, нам не нужно будет использовать завершающие

172:     значения NULL, поэтому оставшаяся часть будет выглядеть аккуратнее

173:     (хотя, честно говоря, менее эффективно). */

174:  job->cmdBuf = command = calloc(1, strlen(*commandPtr) + 1);

175:  job->text = NULL;

176:

177:  prog = job->progs;

178:  prog->numRedirections = 0;

179:  prog->redirections = NULL;

180:  prog->freeGlob = 0;

181:  prog->isStopped = 0;

182:

183:  argvAlloced = 5;

184:  prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);

185:  prog->argv[0] = job->cmdBuf;

186:

187:  buf = command;

188:  src = *commandPtr;

189:  while (*src && !done) {

190:   if (quote == *src) {

191:    quote = '\0';

192:   } else if (quote) {

193:    if (*src ==0 '\\') {

194:     src++;

195:     if (!*src) {

196:      fprintf(stderr,

197:       "после \\ ожидался символ\n");

198:      freeJob(job);

199:      return 1;

200:     }

201:

202:     /* в оболочке сочетание "\'" должно дать */

203:     if (*src != quote) *buf++ = '\\';

204:    } else if (*src == '*' | | *src == ' ?' | | *src == '[' ||

205:     *src == ']')

206:     *buf++ = '\\';

207:    *buf++ = *src;

208:   } else if (isspace(*src)) {

209:    if (*prog->argv[argc]) {

210:     buf++, argc++;

211:     /* +1 здесь оставляет место для NULL, которое

212:        завершает массив argv */

213:     if ((argc + 1) == argvAlloced) {

214:      argvAlloced += 5;

215:      prog->argv = realloc(prog->argv,

216:       sizeof(*prog->argv) * argvAlloced);

217:     }

218:     prog->argv[argc] = buf;

219:

220:     globLastArgument(prog, &argc, &argvAlloced);

221:    }

222:   } else switch (*src) {

223:   case '"':

224:   case '\'':

225:    quote = *src;

226:    break;

227:

228:   case '#': /* комментарий */

229:    done = 1;

230:    break;

231:

232:   case '>': /* переадресации */

233:   case '<':

234:    i = prog->numRedirections++;

235:    prog->redirections = realloc(prog->redirections,

236:    sizeof(*prog->redirections) * (i+1));

237:

238:    prog->redirections[i].fd= -1;

239:    if (buf != prog->argv[argc]) {

240:     /* перед этим символом может быть указан номер

241:        переадресовываемого файла */

242:     prog->redirections[i].fd =

243:      strtol(prog->argv[argc], &chptr, 10);

244:

245:     if (*chptr && *prog->argv[argc]) {

246:      buf++, argc++;

247:      globLastArgument(prog, &argc, &argvAlloced);

248:     }

249:    }

250:

251:    if (prog->redirections[i].fd == -1) {

252:     if (*src == '>')

253:      prog->redirections[i].fd = 1;

254:     else

255:      prog->redirections[i].fd = 0;

256:    }

257:

258:    if (*src++ == '>') {

259:     if (*src == '>') {

260:      prog->redirections[i].type = REDIRECT_APPEND;

261:      src++;

262:     } else {

263:      prog->redirections[i].type = REDIRECT_OVERWRIТЕ;

264:     }

265:    } else {

266:     prog->redirections[i].type = REDIRECT_INPUT;

267:    }

268:

269:    /* Это не соответствует стандарту sh POSIX. Ну и ладно. */

270:    chptr = src;

271:    while (isspace(*chptr)) chptr++;

272:

273:    if (!*chptr) {

274:     fprintf(stderr, "после %c ожидалось имя файла\n",

275:      *src);

276:     freeJob(job);

277:     return 1;

278:    }

279:

280:    prog->redirections[i].filename = buf;

281:    while (*chptr && !isspace(*chptr))

282:     *buf++ = *chptr++;

283:

284:    src = chptr - 1; /* src++ будет сделано позже */

285:    prog->argv[argc] = ++buf;

286:    break;

287:

288:   case '|': /* канал */

289:    /* завершение этой команды */

290:    if (*prog->argv[argc]) argc++;

291:    if (large) {

292:     fprintf(stderr, "пустая команда в канале\n");

293:     freeJob(job);

294:     return 1;

295:    }

296:    prog->argv[argc] = NULL;

297:

298:    /* и начало следующей */

299:    job->numProgs++;

300:    job->progs = realloc(job->progs,

301:     sizeof (*job->progs) *

302:      job->numProgs);

303:    prog = job->progs + (job->numProgs - 1);

304:    prog->numRedirections = 0;

305:    prog->redirections = NULL;

306:    prog->freeGlob = 0;

307:    argc = 0;

308:

309:    argvAlloced = 5;

310:    prog->argv = malloc(sizeof(*prog->argv) *

311:     argvAlloced);

312:    prog->argv[0] = ++buf;

313:

314:    src++;

315:    while (*src && isspace(*src)) src++;

316:

317:    if (!*src) {

318:     fprintf(stderr, "пустая команда в канале\n");

319:     return 1;

320:    }

321:    src--; /* инкремент ++ мы сделаем в конце цикла */

322:

323:    break;

324:

325:   case '&': /* фон */

326:    *isBg = 1;

327:   case ';': /* разнообразные команды */

328:    done = 1;

329:    returnCommand = *commandPtr + (src - * commandPtr) + 1;

330:    break;

331:

332:   case '\\':

333:    src++;

334:    if (!*src) {

335:     freeJob(job);

336:     fprintf(stderr, "после \\ ожидался символ\n");

337:     return 1;

338:    }

339:    if (*src == '*' | | *src == '[' || *src == '] '

340:     || *src == '?')

341:     *buf++ = '\\';

342:    /* неудача */

343:   default:

344:    *buf++ = *src;

345:   }

346:

347:   src++;

348:  }

349:

350:  if (*prog->argv[argc]) {

351:   argc++;

352:   globLastArgument(prog, &argc, &argvAlloced);

353:  }

354:  if (!argc) {

355:   freeJob(job);

356:   return 0;

357:  }

358:  prog->argv[argc] = NULL;

359:

360:  if (!returnCommand) {

361:   job->text = malloc(strlen(*commandPtr) + 1);

362:   strcpy(job->text, *commandPtr);

363:  } else {

364:   /*Оставляем любые хвостовые пробелы, хотя и получится это несколько небрежно*/

365:

366:   count = returnCommand - *commandPtr;

367:   job->text = malloc(count + 1);

368:   strncpy(job->text, *commandPtr, count);

369:   job->text[count] = '\0';

370:  }

371:

372:  *commandPtr = returnCommand;

373:

374:  return 0;

375: }

376:

377: int setupRedirections(struct childProgram * prog) {

378:  int i;

379:  int openfd;

380:  int mode;

381:  struct redirectionSpecifier * redir = prog->redirections;

382:

383:  for (i = 0; i < prog->numRedirections; i++, redir++) {

384:   switch (redir->type) {

385:   case REDIRECT_INPUT:

386:    mode = O_RDONLY;

387:    break;

388:   case REDIRECT_OVERWRITE:

389:    mode = O_RDWR | O_CREAT | O_TRUNC;

390:    break;

391:   case REDIRECT_APPEND:

392:    mode = O_RDWR | O_CREAT | O_APPEND;

393:    break;

394:   }

395:

396:   openfd = open(redir->filename, mode, 0666);

397:   if (openfd < 0) {

398:    /* мы могли потерять это в случае переадресации stderr,

399:       хотя bash и ash тоже потеряют его (а вот

400:       zsh - нет!) */

401:    fprintf(stderr, "ошибка при открытии %s: %s\n",

402:    redir->filename, strerror(errno));

403:    return 1;

404:   }

405:

406:   if (openfd != redir->fd) {

407:    dup2(openfd, redir->fd);

408:    close(openfd);

409:   }

410:  }

411:

412:  return 0;

413: }

414:

415: int runCommand(struct job newJob, struct jobSet * jobList,

416:  int inBg) {

417:  struct job * job;

418:  char * newdir, * buf;

419:  int i, len;

420:  int nextin, nextout;

421:  int pipefds[2]; /* pipefd[0] предназначен для чтения */

422:  char * statusString;

423:  int jobNum;

424:  int controlfds[2] ;/*канал для возможности приостановки работы дочернего процесса*/

425:

426:  /* здесь производится обработка встраиваемых модулей — мы не используем fork(),

427:     поэтому, чтобы поместить процесс в фон, придется потрудиться */

428:  if (!strcmp(newJob.progs[0].argv[0], "exit")) {

429:   /* здесь возвращается реальный код выхода */

430:   exit(0);

431:  } else if (!strcmp(newJob.progs[0].argv[0], "pwd")) {

432:   len = 50;

433:   buf = malloc(len);

434:   while (!getcwd(buf, len) && errno == ERANGE) {

435:    len += 50;

436:    buf = realloc(buf, len);

437:   }

438:   printf("%s\n", buf);

439:   free(buf);

440:   return 0;

441:  } else if (!strcmp(newJob.progs[0].argv[0], "cd")) {

442:   if (!newJob.progs[0].argv[1] == 1)

443:    newdir == getenv("HOME");

444:   else

445:    newdir = newJob.progs[0].argv[1];

446:   if (chdir(newdir))

447:    printf("сбой при смене текущего каталога: %s\n",

448:     strerror(errno));

449:   return 0;

450:  } else if (!strcmp(newJob.progs[0].argv[0], "jobs")) {

451:   for (job = jobList->head; job; job = job->next) {

452:    if (job->runningProgs == job->stoppedProgs)

453:     statusString = "Остановлено";

454:    else

455:     statusString = "Выполняется";

456:

457:    printf(JOB_STATUS_FORMAT, job->jobId, statusString,

458:     job->text);

459:   }

460:   return 0;

461:  } else if (!strcmp(newJob.progs[0].argv[0], "fg") ||

462:   !strcmp(newJob.progs[0].argv[0], "bg")) {

463:   if (!newJob.progs[0].argv[1] || newJob.progs[0].argv[2]) {

464:    fprintf(stderr,

465:     "%s: ожидался в точности один аргумент\n",

466:     newJob.progs[0].argv[0]);

467:    return 1;

468:   }

469:

470:   if (sscanf(newJob.progs[0].argv[1], "%%%d", &jobNum) != 1) {

471:    fprintf(stderr, "%s: неверный аргумент '%s'\n",

472:     newJob.progs[0].argv[0],

473:     newJob.progs[0].argv[1]);

474:    return 1;

475:   }

476:

477:   for (job = jobList->head; job; job = job->next)

478:    if (job->jobId == jobNum) break;

479:

480:   if (!job) {

481:    fprintf(stderr, "%s: неизвестное задание %d\n",

482:     newJob.progs[0].argv[0], jobNum);

483:    return 1;

484:   }

485:

486:   if (*new Job. progs[0].argv[0] == 'f') {

487:    /* Переводим задание на передний план */

488:

489:    if (tcsetpgrp(0, job->pgrp))

490:     perror("tcsetpgrp");

491:    jobList->fg = job;

492:   }

493:

494:   /* Повторяем запуск процессов в задании */

495:   for (i = 0; inumProgs; i++)

496:    job->progs[i].isStopped = 0;

497:

498:   kill(-job->pgrp, SIGCONT);

499:

500:   job->stoppedProgs = 0;

501:

502:   return 0;

503:  }

504:

505:  nextin = 0, nextout = 1;

506:  for (i = 0; i < newJob.numProgs; i++) {

507:   if ((i + 1) < newJob.numProgs) {

508:    pipe(pipefds);

509:    nextout = pipefds[1];

510:   } else {

511:    nextout = 1;

512:   }

513:

514:   pipe(controlfds);

515:

516:   if (!(newJob.progs[i].pid = fork())) {

517:    signal(SIGTTOU, SIG_DFL);

518:

519:    close(controlfds[1]);

520:    /* при чтении будет возвращен 0, когда записывающая сторона закрыта */

521:    read(controlfds[0], &len, 1);

522:    close(controlfds[0]);

523:

524:    if (nextin != 0) {

525:     dup2(nextin, 0);

526:     close(nextin);

527:    }

528:

529:    if (nextout != 1) {

530:     dup2(nextout, 1);

531:     close(nextout);

532:    }

533:

534:    /* явные переадресации подменяют каналы */

535:    setupRedirections(newJob.progs + i);

536:

537:    execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);

538:    fprintf(stderr, "сбой exec() для %s: %s\n",

539:    newJob.progs[i].argv[0],

540:    strerror(errno));

541:    exit(1);

542:   }

543:

544:   /* помещаем дочерний процесс в группу процессов, лидером в которой

545:      является первый процесс в этом канале */

546:   setpgid(newJob.progs[i].pid, newJob.progs[0].pid);

547:

548:   /* закрываем канал управления, чтобы продолжить работу дочернего процесса */

549:   close(controlfds[0]);

550:   close(controlfds[1]);

551:

552:   if (nextin !=0) close(nextin);

553:   if (nextout !=1) close(nextout);

554:

555:   /* Если другого процесса нет, то nextin является "мусором",

556:      хотя это и не является помехой */

557:   nextin = pipefds[0];

558:  }

559:

560:  newJob.pgrp = newJob.progs[0].pid;

561:

562:  /* поиск идентификатора используемого задания */

563:  newJob.jobld = 1;

564:  for (job = jobList->head; job; job = job->next)

565:   if (job->jobId> = newJob.jobId)

566:    newJob.jobId = job->jobId + 1;

567:

568:  /* добавляем задание в список выполняющихся заданий */

569:  if (!jobList->head) {

570:   job = jobList->head = malloc(sizeof(*job));

571:  } else {

572:   for (job = jobList->head; job->next; job = job->next);

573:   job->next = malloc(sizeof(*job));

574:   job = job->next;

575:  }

576:

577:  *job = newJob;

578:  job->next = NULL;

579:  job->runningProgs = job->numProgs;

580:  job->stoppedProgs = 0;

581:

582:  if (inBg) {

583:   /* мы не ожидаем возврата фоновых заданий - добавляем их

584:      в список фоновых заданий и оставляем их */

585:

586:   printf("[%d] %d\n", job->jobId,

587:    newJob.progs[newJob.numProgs - 1].pid);

588:  } else {

589:   jobList->fg = job;

590:

591:   /* перемещаем новую группу процессов на передний план */

592:

593:   if (tcsetpgrp(0, newJob.pgrp))

594:    perror("tcsetpgrp");

595:  }

596:

597:  return 0;

598: }

599:

600: void removeJob(struct jobSet * jobList, struct job * job) {

601:  struct job * prevJob;

602:

603:  freeJob(job);

604:  if (job == jobList->head) {

605:   jobList->head = job->next;

606:  } else {

607:   prevJob = jobList->head;

608:   while (prevJob->next != job) prevJob = prevJob->next;

609:   prevJob->next = job->next;

610:  }

611:

612:  free(job);

613: }

614:

615: /* Проверяем, завершился ли какой-либо фоновый процесс - если да, то

616:    устанавливаем причину и проверяем, окончилось ли выполнение задания */

617: void checkJobs(struct jobSet * jobList) {

618:  struct job * job;

619:  pid_t childpid;

620:  int status;

621:  int progNum;

622:  char * msg;

623:

624:  while ((childpid = waitpid(-1, &status,

625:   WNOHANG | WUNTRACED)) > 0) {

626:   for (job = jobList->head; job; job = job->next) {

627:    progNum = 0;

628:    while(progNum < job->numProgs &&

629:     job->progs[progNum].pid != childpid)

630:     progNum++;

631:    if (progNum < job->numProgs) break;

632:   }

633:

634:   if (WIFEXITED(status) || WIFSIGNALED(status)) {

635:    /* дочерний процесс завершил работу */

636:    job->runningProgs--;

637:    job->progs[progNum].pid = 0;

638:

639:    if (!WIFSIGNALED(status))

640:     msg = "Завершено";

641:    else

642:     msg = strsignal(WTERMSIG(status));

643:

644:    if (!job->runningProgs) {

645:     printf(JOB_STATUS_FORMAT, job->jobId,

646:      msg, job->text);

647:     removeJob(jobList, job);

648:    }

649:   } else {

650:    /* выполнение дочернего процесса остановлено */

651:    job->stoppedProgs++;

652:    job->progs[progNum].isStopped = 1;

653:

654:    if (job->stoppedProgs == job->numProgs) {

655:     printf(JOB_STATUS_FORMAT, job->jobId, "Остановлено",

656:      job->text);

657:    }

658:   }

659:  }

660:

661:  if (childpid == -1 && errno != ECHILD)

662:   perror("waitpid");

663: }

664:

665: int main(int argc, const char ** argv) {

666:  char command[MAX_COMMAND_LEN + 1];

667:  char * nextCommand = NULL;

668:  struct jobSet jobList = { NULL, NULL };

669:  struct job newJob;

670:  FILE * input = stdin;

671:  int i;

672:  int status;

673:  int inBg;

674:

675:  if (argc > 2) {

676:   fprintf(stderr, "неожиданный аргумент; использование: ladsh1 "

677:    "<команды>\n");

678:   exit(1);

679:  } else if (argc == 2) {

680:   input = fopen(argv[1], "r");

681:   if (!input) {

682:    perror("fopen");

683:    exit(1);

684:   }

685:  }

686:

687:  /* не обращаем внимания на этот сигнал; это просто помеха,

688:     не имеющая никакого значения для оболочки */

689:  signal(SIGTTOU, SIG_IGN);

690:

691:  while (1) {

692:   if (!jobList.fg) {

693:    /* на переднем плане нет ни одного задания */

694:

695:    /* проверяем, не завершилось выполнение какого-либо фонового задания */

696:    checkJobs(&jobList);

697:

698:    if (!nextCommand) {

699:     if (getCommand(input, command)) break;

700:     nextCommand = command;

701:    }

702:

703:    if (!parseCommand(&nextCommand, &newJob, &inBg) &&

704:     newJob.numProgs) {

705:     runCommand(newJob, &jobList, inBg);

706:    }

707:   } else {

708:    /* задание выполняется на переднем плане; ожидаем, пока оно завершится */

709:    i = 0;

710:    while (!jobList:fg->progs[i].pid ||

711:     jobList.fg->progs[i].isStopped) i++;

712:

713:    waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);

714:

715:    if (WIFSIGNALED(status) &&

716:     (WTERMSIG(status) != SIGINT)) {

717:     printf("%s\n", strsignal(status));

718:    }

719:

720:    if (WIFEXITED(status) || WIFSIGNALED(status)) {

721:     /* дочерний процесс завершил работу */

722:     jobList.fg->runningProgs--;

723:     jobList.fg->progs[i].pid = 0;

724:

725:     if (!jobList.fg->runningProgs) {

726:      /* дочерний процесс завершил работу */

727:

728:      removeJob(&jobList, jobList.fg);

729:      jobList.fg = NULL;

730:

731:      /* переводим оболочку на передний план */

732:      if (tcsetpgrp(0, getpid()))

733:       perror("tcsetpgrp");

734:     }

735:    } else {

736:     /* выполнение дочернего процесса было остановлено */

737:     jobList.fg->stoppedProgs++;

738:     jobList.fg->progs[i].isStopped = 1;

739:

740:     if (jobList.fg->stoppedProgs ==

741:      jobList.fg->runningProgs) {

742:      printf("\n" JOB_STATUS_FORMAT,

743:       jobList.fg->jobId,

744:        "Остановлено", jobList.fg->text);

745:        jobList.fg = NULL;

746:     }

747:    }

748:

749:    if (!jobList.fg) {

750:     /* переводим оболочку на передний план */

751:     if (tcsetpgrp(0, getpid()))

752:      perror("tcsetpgrp");

753:    }

754:   }

755:  }

756:

757:  return 0;

758: }