Remove old lock/release cases from control.c + hopefully silence warnings on 64 bit...
[matches/MCTX3420.git] / server / control.c
1 /**
2  * @file control.c
3  * @brief Handles all client control requests (admin related)
4  */
5 #include "common.h"
6 #include "control.h"
7 #include "sensor.h"
8 #include "actuator.h"
9
10 typedef struct ControlData {
11         ControlModes current_mode;
12         pthread_mutex_t mutex;
13         struct timeval start_time;
14 } ControlData;
15
16 ControlData g_controls = {CONTROL_STOP, PTHREAD_MUTEX_INITIALIZER, {0}};
17
18 bool PathExists(const char *path) 
19 {
20         FILE *fp = fopen(path, "r");
21         if (fp) {
22                 fclose(fp);
23                 return true;
24         }
25         return false;
26 }
27
28 /**
29  * System control handler. This covers high-level control, including
30  * admin related functions and starting/stopping experiments..
31  * @param context The context to work in
32  * @param params The input parameters
33  */
34 void Control_Handler(FCGIContext *context, char *params) {
35         const char *action = "";
36         const char *name = "";
37         bool force = false;
38         ControlModes desired_mode;
39
40
41
42         // Login/auth now handled entirely in fastcgi.c and login.c
43         //TODO: Need to not have the ability for any user to stop someone else' experiment...
44         // (achieve by storing the username of the person running the current experiment, even when they log out?)
45         // (Our program should only realisitically support a single experiment at a time, so that should be sufficient)
46         FCGIValue values[4] = {
47                 {"action", &action, FCGI_REQUIRED(FCGI_STRING_T)},
48                 {"force", &force, FCGI_BOOL_T},
49                 {"name", &name, FCGI_STRING_T}
50         };
51
52         if (!FCGI_ParseRequest(context, params, values, 4))
53                 return;
54         
55         if (!strcmp(action, "emergency")) {
56                 desired_mode = CONTROL_EMERGENCY;
57         } else if (!strcmp(action, "start")) {
58                 desired_mode = CONTROL_START;
59         } else if (!strcmp(action, "pause")) {
60                 desired_mode = CONTROL_PAUSE;
61         } else if (!strcmp(action, "resume")) {
62                 desired_mode = CONTROL_RESUME;
63         } else if (!strcmp(action, "stop")) {
64                 desired_mode = CONTROL_STOP; 
65         } else {
66                 FCGI_RejectJSON(context, "Unknown action specified.");
67                 return;
68         }
69         
70         void *arg = NULL;
71         if (desired_mode == CONTROL_START) {
72                 if (PathExists(name) && !force) {
73                         FCGI_RejectJSON(context, "An experiment with that name already exists.");
74                         return;
75                 }
76
77                 arg = (void*)name;
78         }
79
80         const char *ret;
81         if ((ret = Control_SetMode(desired_mode, arg)) != NULL) {
82                 FCGI_RejectJSON(context, ret);
83         } else {
84                 FCGI_BeginJSON(context, STATUS_OK);
85                 FCGI_JSONPair("description", "ok");
86                 FCGI_EndJSON();
87         }
88 }
89
90 /**
91  * Sets the mode to the desired mode, if possible.
92  * @param desired_mode The mode to be set to
93  * @param arg An argument specific to the mode to be set. 
94  * @return NULL on success, an error message on failure.
95  */
96 const char* Control_SetMode(ControlModes desired_mode, void * arg)
97 {
98         const char *ret = NULL;
99
100         pthread_mutex_lock(&(g_controls.mutex));
101         if (g_controls.current_mode == desired_mode)
102                 ret = "Already in the desired mode.";
103         else if (g_controls.current_mode == CONTROL_EMERGENCY && desired_mode != CONTROL_STOP)
104                 ret = "In emergency mode. You must stop before continuing.";
105         else switch (desired_mode) {
106                 case CONTROL_START:
107                         if (g_controls.current_mode == CONTROL_STOP) {
108                                 const char * name = arg;
109                                 if (!*name)
110                                         ret = "An experiment name must be specified";
111                                 else if (strpbrk(name, INVALID_CHARACTERS))
112                                         ret = "The experiment name must not contain: " INVALID_CHARACTERS_JSON;
113                                 else {
114                                         FILE *fp = fopen((const char*) arg, "a");
115                                         if (fp) {
116                                                 fclose(fp);
117                                                 gettimeofday(&(g_controls.start_time), NULL);
118                                         } else
119                                                 ret = "Cannot open experiment name marker";
120                                 }
121                         } else 
122                                 ret = "Cannot start when not in a stopped state.";
123                 break;
124                 case CONTROL_PAUSE:
125                         if (g_controls.current_mode != CONTROL_START)
126                                 ret = "Cannot pause when not in a running state.";
127                 break;
128                 case CONTROL_RESUME:
129                         if (g_controls.current_mode != CONTROL_PAUSE)
130                                 ret = "Cannot resume when not in a paused state.";
131                 break;
132                 case CONTROL_EMERGENCY:
133                         if (g_controls.current_mode != CONTROL_START) //pfft
134                                 ret = "Not running so how can there be an emergency.";
135                 break;
136                 default:
137                 break;
138         }
139         
140         if (ret == NULL) {
141                 Actuator_SetModeAll(desired_mode, arg);
142                 Sensor_SetModeAll(desired_mode, arg);
143                 if (desired_mode != CONTROL_RESUME)
144                         g_controls.current_mode = desired_mode;
145                 else
146                         g_controls.current_mode = CONTROL_START;
147         }
148         pthread_mutex_unlock(&(g_controls.mutex));
149         return ret;
150 }
151
152 /**
153  * Gets a string representation of the current mode
154  * @param mode The mode to get a string representation of
155  * @return The string representation of the mode
156  */
157 const char * Control_GetModeName() {
158         const char * ret = "Unknown";
159
160         switch (g_controls.current_mode) {
161                 case CONTROL_START: ret = "Running"; break;
162                 case CONTROL_PAUSE: ret = "Paused"; break;
163                 case CONTROL_RESUME: ret = "Resumed"; break;
164                 case CONTROL_STOP: ret = "Stopped"; break;
165                 case CONTROL_EMERGENCY: ret = "Emergency mode"; break;
166         }
167         return ret;
168 }
169
170 /**
171  * Gets the start time for the current experiment
172  * @return the start time
173  */
174 const struct timeval* Control_GetStartTime() {
175         return &g_controls.start_time;
176 }

UCC git Repository :: git.ucc.asn.au