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] = ' ';
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: %sn",
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("%sn", 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("сбой при смене текущего каталога: %sn",
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: неизвестное задание %dn",
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; i<job->numProgs; 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: %sn",
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] %dn", 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: