CS360 Makeup Final Exam - May 4, 2012 - James S. Plank

Answer all questions on the answer sheet. When you write code, you do not need to include any include files.

I would prefer, if you have time, that you rewrite your code to make it neat. However, if you don't have time, I understand.

The grading on these questions will be roughly 12 points for question 1, 10 for question 2, 12 for question 3, and 24 for question 4.


Question 1

Explain the exact semantics of the following system calls. Also explain how/why each system call is typically used.

Question 2

You work in the IT department of a medium size investment company. Your boss paid $100,000 for a real-time stock quote program called ripoff. It is called from the command line with a host and port number of a quote server, and a query time (in seconds). It then runs with a loop that is equivalent to read_quotes() in the C code below:

typedef struct {
  char *host;
  int port;
  int qtime;
  FILE *fin;
  FILE *fout;
  int socket;
} Ripoff_Info;
void establish(Ripoff_Info *r, int cl)
{
  if (cl) {
    fclose(r->fin);
    fclose(r->fout);
    close(r->socket);
  }
  r->socket = 
     request_connection(r->host, r->port);
  r->fin = fdopen(r->socket, "r");
  r->fout = fdopen(r->socket, "w");
}
void read_quotes(Ripoff_Info *r)
{
  establish(r, 0);

  while (1) {
    sleep(r->qtime);
    fprintf(r->fout, "GET-QUOTES\n");
    fflush(r->fout) != 0)
    if (read_and_display_quotes(r->fin) == 0) {
      establish(r, 1);
    }
  }
}

The procedure read_and_display_quote() reads quotes from their server and displays them using sophisticated graphics. It returns 1 when it's done. If the connection to the server dies during read_and_display_quote(), then it returns zero, and as you can see, the program re-establishes a connection to the server, and continues.

Part A: Your boss only received a precompiled executable for his $100,000. He was told to run it with the following command line arguments:

UNIX> ripoff server.ripoff.com 5555 1
The problem is that the program keeps dying after 10 minutes. Your boss asks you to figure out why, and when you call customer service, they tell you, "server.ripoff.com never keeps its connections open for more than 10 minutes to prevent denial of service attacks. It's ok, because the program re-establishes lost connections." Customer service is wrong, and it has nothing to do with denial of service attacks. Tell me why your program is dying -- give me the exact situation: tell me when and where the program is dying.

Part B: If you had the source code, you could fix this problem very easily. Go ahead and fix it. The "answer sheet for Question 1" has the above code in it -- go ahead and modify it so that it fixes the problem and re-establishes the connection rather than dying.

You may not add any global variables to this program. Fix it in the way that was advocated in class.









Question 3

Unfortunately, your boss didn't get the source code for ripoff.c -- that would have cost an extra $100,000. Alternatively, customer service has informed you that for an extra $50,000, ripoff.com will allocate you your very own server that doesn't drop connections. Your boss is too cheap for either of those. He suggests to you the following:

Write a program with two threads, A and B. Have thread A call execl("./ripoff", "ripoff", "server.ripoff.com", "5555", "1", NULL) and have thread B call pthread_join() on thread A. When the server drops the connection and ripoff dies, thread A will die too, and thread B can fork off a new thread A to reestablish the connection.

Part A: While tact may not be your strong suit, you need to tell your boss that what he is proposing is impossible. Please explain to me why the boss's plan is impossible. Be specific. You don't have to be tactful.

Part B: You can use fork(), execl() and wait() to do pretty much the same thing that your boss wants you to do, without bothering with pthreads. Write the program that does this. The only system calls that you are allowed to make, implicitly or explicitly, are fork(), execl() and wait().


Question 4

Part A: In another cost-cutting coup, your boss has purchased a new machine to run ripoff. The machine was free. However, in order to purchase it, your department will be charged $10 for every time that the fork() system call is made on the machine. The solution in Question 2 is now going to cost your department $1,440 per day (it monitors quotes all over the world, so it has to be on 24/7).

You've been offered a raise if you can figure out a way to fix this problem, and since you've taken CS360, you're in good shape. You need to write a proxy server, which will run on the same machine as ripoff: dumbboss.investers.com. It will serve a socket on port 6000, and you will call ripoff as follows:

UNIX> ripoff dumbboss.inverstors.com 6000 1
Your proxy will make sure that ripoff never dies, by re-establishing its own connection to server.ripoff.com when it detects that the connection has gone away. In that way, the connection between ripoff and the proxy never goes away. Your job is to write the proxy as a pthreads program with two threads. One thread reads from ripoff and writes to the server, and one reads from the server and writes to ripoff.

I have written a lot of this program for you on the next page -- your job is to simply write the procedures for the two threads: ripoff_to_server() and server_to_ripoff(). As you can see, they both get the same information. You can add to Thread_Info, but you will lose two points if you do so.

You may assume that output from server.ripoff.com is line buffered (in other words, it always comes as full lines with a newline, which means that you can use fdopen()/fgets()/fputs()). Moreover, lines are never more than 500 characters long.

Also, read Part C before you start implementing.

Part B (2 points, probably): Why do I call pthread_exit() in the main thread rather than just letting it exit naturally?

Part C: There are four ways that connections can die:

  1. The read end of fd_ripoff goes away. In this case, your program should exit.
  2. The write end of fd_ripoff goes away. In this case, your program should exit.
  3. The read end of fd_server goes away. In this case, your program should re-establish the connection to the server.
  4. The write end of fd_server goes away. In this case, your program should re-establish the connection to the server, and you should re-send the "GET-QUOTE\n" line to the server.
In each of the four cases, state how your program achieves its goals: where and how is the condition detected, and how is it handled? If your program doesn't achieve its goal, state what it does so that you can get partial credit.





The code for question 4:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct {
  FILE *fin_server;
  FILE *fout_server;
  int fd_server;
  FILE *fin_ripoff;
  FILE *fout_ripoff;
  int fd_ripoff;
  pthread_mutex_t *lock;
  pthread_cond_t *cond;
} Thread_Info;

void *ripoff_to_server(void *arg)
{
  Thread_Info *ti;
  
  ti = (Thread_Info *) arg;

  /* You write this. */
}

void *server_to_ripoff(void *arg)
{
  Thread_Info *ti;

  ti = (Thread_Info *) arg;

  /* You write this. */
}

main()
{
  int s;
  Thread_Info TI;
  pthread_t rts, str;
  pthread_mutex_t lock;
  pthread_cond_t cond;

  s = serve_socket(6000);
  TI.fd_ripoff = accept_connection(s);
  close(s);

  TI.fin_ripoff = fdopen(TI.fd_ripoff, "r");
  TI.fout_ripoff = fdopen(TI.fd_ripoff, "w");

  pthread_mutex_init(&lock, NULL);
  TI.lock = &lock;
  pthread_cond_init(&cond, NULL);
  TI.cond = &cond;

  TI.fd_server = request_connection("server.ripoff.com", 5555);
  TI.fin_server = fdopen(TI.fd_server, "r");
  TI.fout_server = fdopen(TI.fd_server, "w");

  pthread_create(&rts, NULL, ripoff_to_server, &TI);
  pthread_create(&str, NULL, server_to_ripoff, &TI);
  pthread_exit(NULL);
}