4b4a1e6e2f15473861b2a3629602982a2b399e13
[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, *key = "", *name = "";
36         bool force = false;
37         ControlModes desired_mode;
38
39         FCGIValue values[4] = {
40                 {"action", &action, FCGI_REQUIRED(FCGI_STRING_T)},
41                 {"key", &key, FCGI_STRING_T},
42                 {"force", &force, FCGI_BOOL_T},
43                 {"name", &name, FCGI_STRING_T}
44         };
45
46         if (!FCGI_ParseRequest(context, params, values, 4))
47                 return;
48         
49         if (!strcmp(action, "lock")) {
50                 FCGI_LockControl(context, force);
51                 return;
52         } else if (!strcmp(action, "emergency")) {
53                 desired_mode = CONTROL_EMERGENCY;
54         } else if (FCGI_HasControl(context, key)) {
55                 if (!strcmp(action, "release")) {
56                         FCGI_ReleaseControl(context);
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         } else {
70                 FCGI_RejectJSONEx(context, STATUS_UNAUTHORIZED, 
71                         "Invalid control key specified.");
72                 return;
73         }
74
75         void *arg = NULL;
76         if (desired_mode == CONTROL_START) {
77                 if (PathExists(name) && !force) {
78                         FCGI_RejectJSON(context, "An experiment with that name already exists.");
79                         return;
80                 }
81
82                 arg = (void*)name;
83         }
84
85         const char *ret;
86         if ((ret = Control_SetMode(desired_mode, arg)) != NULL) {
87                 FCGI_RejectJSON(context, ret);
88         } else {
89                 FCGI_BeginJSON(context, STATUS_OK);
90                 FCGI_JSONPair("description", "ok");
91                 FCGI_EndJSON();
92         }
93 }
94
95 /**
96  * Sets the mode to the desired mode, if possible.
97  * @param desired_mode The mode to be set to
98  * @param arg An argument specific to the mode to be set. 
99  * @return NULL on success, an error message on failure.
100  */
101 const char* Control_SetMode(ControlModes desired_mode, void * arg)
102 {
103         const char *ret = NULL;
104
105         pthread_mutex_lock(&(g_controls.mutex));
106         if (g_controls.current_mode == desired_mode)
107                 ret = "Already in the desired mode.";
108         else if (g_controls.current_mode == CONTROL_EMERGENCY && desired_mode != CONTROL_STOP)
109                 ret = "In emergency mode. You must stop before continuing.";
110         else switch (desired_mode) {
111                 case CONTROL_START:
112                         if (g_controls.current_mode == CONTROL_STOP) {
113                                 const char * name = arg;
114                                 if (!*name)
115                                         ret = "An experiment name must be specified";
116                                 else if (strpbrk(name, INVALID_CHARACTERS))
117                                         ret = "The experiment name must not contain: " INVALID_CHARACTERS_JSON;
118                                 else {
119                                         FILE *fp = fopen((const char*) arg, "a");
120                                         if (fp) {
121                                                 fclose(fp);
122                                                 gettimeofday(&(g_controls.start_time), NULL);
123                                         } else
124                                                 ret = "Cannot open experiment name marker";
125                                 }
126                         } else 
127                                 ret = "Cannot start when not in a stopped state.";
128                 break;
129                 case CONTROL_PAUSE:
130                         if (g_controls.current_mode != CONTROL_START)
131                                 ret = "Cannot pause when not in a running state.";
132                 break;
133                 case CONTROL_RESUME:
134                         if (g_controls.current_mode != CONTROL_PAUSE)
135                                 ret = "Cannot resume when not in a paused state.";
136                 break;
137                 case CONTROL_EMERGENCY:
138                         if (g_controls.current_mode != CONTROL_START) //pfft
139                                 ret = "Not running so how can there be an emergency.";
140                 break;
141                 default:
142                 break;
143         }
144         
145         if (ret == NULL) {
146                 Actuator_SetModeAll(desired_mode, arg);
147                 Sensor_SetModeAll(desired_mode, arg);
148                 if (desired_mode != CONTROL_RESUME)
149                         g_controls.current_mode = desired_mode;
150                 else
151                         g_controls.current_mode = CONTROL_START;
152         }
153         pthread_mutex_unlock(&(g_controls.mutex));
154         return ret;
155 }
156
157 /**
158  * Gets a string representation of the current mode
159  * @param mode The mode to get a string representation of
160  * @return The string representation of the mode
161  */
162 const char * Control_GetModeName() {
163         const char * ret = "Unknown";
164
165         switch (g_controls.current_mode) {
166                 case CONTROL_START: ret = "Running"; break;
167                 case CONTROL_PAUSE: ret = "Paused"; break;
168                 case CONTROL_RESUME: ret = "Resumed"; break;
169                 case CONTROL_STOP: ret = "Stopped"; break;
170                 case CONTROL_EMERGENCY: ret = "Emergency mode"; break;
171         }
172         return ret;
173 }
174
175 /**
176  * Gets the start time for the current experiment
177  * @return the start time
178  */
179 const struct timeval* Control_GetStartTime() {
180         return &g_controls.start_time;
181 }

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