aboutsummaryrefslogtreecommitdiffstats
path: root/P3/server.c
blob: a037f2ab1dfbcbf91c653cc01f02dbe9693f6076 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/time.h>
#include <time.h>
#include "util.h"
#include <stdbool.h>
#include <unistd.h>
#include <signal.h>

#define MAX_THREADS 100
#define MAX_queue_len 100
#define MAX_CE 100
#define INVALID -1
#define BUFF_SIZE 1024

/*
  THE CODE STRUCTURE GIVEN BELOW IS JUST A SUGGESTION. FEEL FREE TO MODIFY AS NEEDED
*/

// structs:
typedef struct request_queue {
   int fd;
   char *request;
} request_t;

typedef struct cache_entry {
    int len;
    char *request;
    char *content;
} cache_entry_t;

/* ******************** Dynamic Pool Code  [Extra Credit A] **********************/
// Extra Credit: This function implements the policy to change the worker thread pool dynamically
// depending on the number of requests
void * dynamic_pool_size_update(void *arg) {
  while(1) {
    // Run at regular intervals
    // Increase / decrease dynamically based on your policy
  }
}
/**********************************************************************************/

/* ************************ Cache Code [Extra Credit B] **************************/

// Function to check whether the given request is present in cache
int getCacheIndex(char *request){
  /// return the index if the request is present in the cache
}

// Function to add the request and its file content into the cache
void addIntoCache(char *mybuf, char *memory , int memory_size){
  // It should add the request at an index according to the cache replacement policy
  // Make sure to allocate/free memory when adding or replacing cache entries
}

// clear the memory allocated to the cache
void deleteCache(){
  // De-allocate/free the cache memory
}

// Function to initialize the cache
void initCache(){
  // Allocating memory and initializing the cache array
}

/**********************************************************************************/

/* ************************************ Utilities ********************************/
// Function to get the content type from the request
char* getContentType(char * mybuf) {
  // Should return the content type based on the file type in the request
  // (See Section 5 in Project description for more details)

  
}

// Function to open and read the file from the disk into the memory
// Add necessary arguments as needed
int readFromDisk(/*necessary arguments*/) {
    // Open and read the contents of file given the request
}

/**********************************************************************************/

// Function to receive the request from the client and add to the queue
void * dispatch(void *arg) {

  while (1) {

    // Accept client connection

    // Get request from the client

    // Add the request into the queue

   }
   return NULL;
}

/**********************************************************************************/

// Function to retrieve the request from the queue, process it and then return a result to the client
void * worker(void *arg) {

   while (1) {

    // Get the request from the queue

    // Get the data from the disk or the cache (extra credit B)

    // Log the request into the file and terminal

    // return the result
  }
  return NULL;
}

/**********************************************************************************/

static volatile sig_atomic_t exitFlag = 0;
int main(int argc, char **argv) {

  // Error check on number of arguments
  if(argc != 8){
    printf("usage: %s port path num_dispatcher num_workers dynamic_flag queue_length cache_size\n", argv[0]);
    return -1;
  }

  // Get the input args
  int port = argv[3];
  char* path = argv[4];
  int dispatchers = argv[5];
  int workers = argv[6];
  int dynFlag = argv[7];
  int qLen = argv[8];
  int cSiz = argv[9];
  // Perform error checks on the input arguments
  if (port < 1025 || port > 65535) {
    perror("Invalid port. Port must be greater than 1024 or less than 65536.\n");
    return -1;
  }
  if (dispatchers > MAX_THREADS || dispatchers < 1) {
    perror("Number of dispatchers is invalid. It must be greater than 0 or less than 101.\n");
    return -1;
  }
  if (workers > MAX_THREADS || workers < 1) {
    perror("Number of dispatchers is invalid. It must be greater than 0 or less than 101.\n");
    return -1;
  }
  if (qLen > MAX_queue_len || qLen <= 0) {
    perror("Queue length is invalid.\n");
    return -1;
  }
  // Change SIGINT action for grace termination
  struct sigaction act;
  sigset_t sigset;
  act.sa_handler = eggs;
  act.sa_flags = 0;
  if (sigemptyset(&act.sa_mask) == -1 ||
      sigaction(SIGINT, &act, NULL) == -1) {
        perror("SIGINT handler failed.\n");
        return -1;
  }
  // Open log file
  FILE* logfile = fopen("serverlog.txt", "a+");
  // Change the current working directory to server root directory
  if (chdir("testing") == -1) {
    perror("Could not find root webserver directory testing. Exiting\n");
    return -1;
  }
  // Initialize cache (extra credit B)

  // Start the server
  init(port);
  // Create dispatcher and worker threads (all threads should be detachable)
  pthread_t dThreads[dispatchers];
  pthread_create(&dThreads, NULL, dispatch, NULL); // DEBUG! figure out last arg

  pthread_t wThreads[workers];
  pthread_create(&wThreads, NULL, worker, NULL); //TODO: Worker arguments.

  // Create dynamic pool manager thread (extra credit A)

  //Server loop (RUNS FOREVER)
  while (1) {
    //TODO: Add something else?

    // Terminate server gracefully
    if (exitFlag){
      printf("SIGINT caught, exiting now (please wait for the threads to die)\n");
      // Print the number of pending requests in the request queue
      // close log file
      fclose(logfile);
      // Remove cache (extra credit B)

      return 0;
    }
    
  }
  printf("This should never be printed.");
  return 42;
}
static void eggs(int signo) {
  exitFlag |= 1;
}