Answered step by step
Verified Expert Solution
Link Copied!

Question

1 Approved Answer

C PROGRAMMING: The Great Ancestor Process (PLEASE WRITE IN C Language!) The only way new processes come into existence in unix is via fork(). However,

C PROGRAMMING: The Great Ancestor Process (PLEASE WRITE IN C Language!)

The only way new processes come into existence in unix is via fork(). However, one process has to be created upon system startup as a special case. This is process ID number 1, and it runs a program called init which is responsible for starting other fundamental system processes.

Init does a bit more than that: It monitors these processes and when one exits, init starts another.

Thus this works well for login-oriented processes: Init starts a program getty which initializes the terminal and prints an appropriate message; when the user types their logname, getty execs the login program (without forking, so the login program has the same process ID number which init is still monitoring); when you log in successfully, login execs your shell (still with the same process ID number); and when you log out, init notices that that process ID number no longer exists and starts a new getty on that terminal.

TASK:

You will write a program much like init, although for testing you will run programs which are slightly more fun than getty. You will read from a configuration file whose name must be specified on the command-line (init defaults to reading /etc/inittab, but that default would be pointless for this assignment). Your program takes an optional r parameter which indicates the run- level, which is a single character whose meaning is described below. If this parameter is not present, the run-level defaults to 3.

Use getopt() to parse the command-line, and output a fatal error message if the subsequent number of file name parameters is not exactly 1.

The configuration file has one entry per line, with fields separated by colons. Comments are introduced by # and run to the end of the line; and blank lines are permitted (that is, they must be ignored, rather than yielding an error message).

If the line is not empty (nor is solely a comment), it contains three colon-separated fields (the real inittabs first field is omitted here):

The first field is a list of which run-levels the given process should be executed for, as a string of individual run-level characters. For example, 23 indicates that the process should be run in run- levels 2 and 3. If this first field on the line is empty, then the line applies to all run-levels. Otherwise, if the current run-level as specified by the r option is not listed, your program will discard this line after parsing.

The second field specifies the re-spawning behaviour for this process. For this assignment, the contents of this field must be either the string once, which means that the process should be executed once and your program will ignore whether or when it terminates; or the string respawn, which indicates that your program should watch for the process to terminate and then start a new one. Any other value is a fatal error.

The third field is a command-line to be executed with sh c string.

You may impose a maximum of 100 entries, but if this is exceeded you must exit with an appropriate error message rather than exceeding array bounds. (In real life we would build a dynamic data structure of some kind, such as a linked list, but you dont have to do that for this assignment; youll do that in assignment four.)

there is an example configuration file, and some sample programs you can list in your configuration file, some of which perform slightly cute services. You do not submit a configuration file; those files are just for your own testing. For our testing, your program will be run with a completely different configuration file and it must obey the instructions in that file. The supplied sample program listener allows you to offer any program as a network service; it takes a command-line parameter saying what program to offer. You must be extremely careful about what programs you use in this manner! Anyone on the planet who connects to that service will be running a program under your linux account with your permissions. Thats why all of the commands in the sample program sillyshell are fake or trivial (in real life, wed make them log in first... and wed be very cautious about any possible security vulnerabilities in that too!). For the parameter to the listener program in your configuration file, I suggest you restrict yourself to /bin/date, simple echo commands

(you cant use strtok() because there can be empty fields; but again, it is a fairly easy parsing operation).

Example 1 (SILLYSHELL):

/*

* This shell-like program doesn't do much, mostly for security reasons.

* That is to say, while fiddling with "listener" and your assignment three

* solution and such, you don't want to be allowing people around the planet

* to execute interesting commands on your UTSC linux account.

*/

#include

#include

#include

#include

#include

extern void date(), fakewho(), fakemail(), help(), logout();

struct {

char *cmd;

void (*fn)();

} cmdlist[] = {

{ "date", date },

{ "who", fakewho },

{ "mail", fakemail },

{ "?", help },

{ "help", help },

{ "logout", logout },

};

int main()

{

char buf[500], *p;

int i;

while (printf("$ "), fflush(stdout), fgets(buf, sizeof buf, stdin)) {

if ((p = strchr(buf, ' ')))

*p = '\0';

if ((p = strchr(buf, ' ')))

*p = '\0';

for (i = 0; i < sizeof cmdlist / sizeof cmdlist[0]; i++)

if (strcmp(cmdlist[i].cmd, buf) == 0)

(*cmdlist[i].fn)();

}

return(0);

}

void date()

{

system("date");

}

void fakewho()

{

printf("bill ttyp1 Feb 26 18:28 (bill.microsoft.com) ");

printf("bill ttyp2 Feb 26 18:29 (bill.microsoft.com) ");

}

void fakemail()

{

char buf[80];

time_t now;

printf("Message 1: ");

(void)time(&now);

printf("From ajr %s", ctime(&now));

printf("To: %s@utoronto.ca ", getenv("USER"));

printf("Subject: thanks ");

printf(" ");

printf("Thanks for running my \"sillyshell\" program. ");

printf(" ");

printf("regards, ");

printf("ajr ");

printf(" Disposition: ");

fflush(stdout);

fgets(buf, sizeof buf, stdin);

printf("Deleted. End of mailbox. ");

}

void help()

{

printf("Please type a command, such as date, who, mail, or logout, ");

printf("and I'll consider executing it. ");

}

void logout()

{

exit(0);

}

Example 2 (LISTENER):

/*

* Listen on a port, accept a connection, execute (argv+1)

* i.e. command line looks like:

* listener [-p port] cmd [arg1 ...]

* Example:

* listener /bin/echo Hello, world

* or

* listener -p 1234 /bin/echo Hello, world

*/

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

int port = 3098;

extern void exitbad();

int main(int argc, char **argv)

{

int c, status = 0;

socklen_t len;

int listenfd, clientfd;

struct sockaddr_in r;

extern char **environ;

while ((c = getopt(argc, argv, "p:")) != EOF) {

if (c == 'p') {

if ((port = atoi(optarg)) == 0) {

fprintf(stderr, "%s: non-numeric port \"number\" ", argv[0]);

exitbad();

}

} else {

status = 1;

}

}

if (status || argc == optind) {

fprintf(stderr, "usage: %s [-p port] ", argv[0]);

exitbad();

}

if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {

perror("socket");

exitbad();

}

c = 1;

if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &c, sizeof c)) {

perror("setsockopt");

exitbad();

}

r.sin_family = AF_INET;

r.sin_addr.s_addr = INADDR_ANY;

r.sin_port = htons(port);

if (bind(listenfd, (struct sockaddr *)&r, sizeof r)) {

perror("bind");

exitbad();

}

if (listen(listenfd, 5)) {

perror("listen");

exitbad();

}

len = sizeof r;

if ((clientfd = accept(listenfd, (struct sockaddr *)&r, &len)) < 0) {

perror("accept");

exitbad();

}

/* redirect all standard file descriptors to client */

dup2(clientfd, 0);

dup2(clientfd, 1);

dup2(clientfd, 2);

close(clientfd);

close(listenfd);

(void)execve(argv[optind], argv + optind, environ);

perror(argv[optind]);

exitbad();

return(0); /* not reached, but shut up gcc -Wall */

}

void exitbad()

{

/*

* Since this will be being invoked from an init-like program, and the

* assignment three specification does not ask the student to implement

* init's re-spawn delay in the event of an apparently-misbehaving

* process, we put in a short delay before exiting in error.

*/

fflush(stdout);

fflush(stderr);

sleep(4);

exit(1);

}

Example 3 (BIFF):

/*

* A "biff"-like program for telling you that you have new mail.

* It takes advantage of being run under "myinit" by exiting when the going

* gets tough. Ok, it's not very tough to deal with the file shrinking, but

* it makes this more useful as a demo program in assignment three.

*/

#include

#include

#include

#include

#include

#define MAIL "/var/mail"

static char *progname;

int main(int argc, char **argv)

{

/* some error-checking functions which always succeed: */

extern FILE *efopen(char *file, char *mode);

extern char *ecat(char *s, char *t);

extern char *egetenv(char *varname);

extern long filesize(FILE *fp); /* note: loses current file position */

extern void efseek(FILE *fp, long pos);

extern void showfrom(FILE *fp); /* show From line info */

FILE *fp;

long oldsize, newsize;

progname = argv[0];

fp = efopen(ecat(MAIL "/", egetenv("USER")), "r");

oldsize = filesize(fp);

while (1) {

sleep(3); /* several minutes would be more reasonable, but you'll

want it short to be able to observe its behaviour */

if ((newsize = filesize(fp)) == oldsize) {

/* no new mail; just sleep */

} else if (newsize > oldsize) {

efseek(fp, oldsize);

printf("New mail from ");

showfrom(fp);

oldsize = newsize;

} else {

printf("File shrunk -- exiting ");

exit(0);

}

}

}

FILE *efopen(char *file, char *mode)

{

FILE *fp;

if ((fp = fopen(file, mode)) == NULL) {

perror(file);

/* pedagogical note: try changing "/var/mail" to a typo such as

* "/vra/mail" and you'll see why we insist that the exact file

* name is passed to perror() rather than a string such as "your

* mailbox"...

*/

exit(1);

}

return(fp);

}

void showfrom(FILE *fp) /* show From line info */

{

int c = getc(fp);

while (c != EOF && !isspace(c) && c != ' ')

c = getc(fp);

while (c != EOF && c != ' ') {

c = getc(fp);

putchar(c);

}

}

char *ecat(char *s, char *t)

{

static char buf[200];

if (strlen(s) + strlen(t) + 1 > sizeof buf) {

fprintf(stderr, "%s: string value is too big ", progname);

exit(1);

}

strcpy(buf, s);

strcat(buf, t);

return(buf);

}

char *egetenv(char *varname)

{

char *p;

if ((p = getenv(varname)) == NULL) {

fprintf(stderr, "%s: %s variable not set in environment ",

progname, varname);

exit(1);

}

return(p);

}

void efseek(FILE *fp, long pos)

{

if (fseek(fp, pos, SEEK_SET)) {

perror("fseek()");

exit(1);

}

}

long filesize(FILE *fp) /* note: loses current file position */

{

if (fseek(fp, 0L, SEEK_END)) {

perror("fseek()");

exit(1);

}

return(ftell(fp));

}

Step by Step Solution

There are 3 Steps involved in it

Step: 1

blur-text-image

Get Instant Access to Expert-Tailored Solutions

See step-by-step solutions with expert insights and AI powered tools for academic success

Step: 2

blur-text-image

Step: 3

blur-text-image

Ace Your Homework with AI

Get the answers you need in no time with our AI-driven, step-by-step assistance

Get Started

Recommended Textbook for

MySQL/PHP Database Applications

Authors: Jay Greenspan, Brad Bulger

1st Edition

978-0764535376

More Books

Students also viewed these Databases questions

Question

2. What is the impact of information systems on organizations?

Answered: 1 week ago

Question

Evaluate the impact of technology on HR employee services.

Answered: 1 week ago