Buggy \0 needed
[handlervirt.git] / migrateoff.c
blob222d6eebe2ecaef1068d6d3a3291b0527b9eec74
1 #include <time.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <assert.h>
6 #include <avahi-client/client.h>
7 #include <avahi-client/publish.h>
9 #include <avahi-common/alternative.h>
10 #include <avahi-common/simple-watch.h>
11 #include <avahi-common/malloc.h>
12 #include <avahi-common/error.h>
13 #include <avahi-common/timeval.h>
15 #include <libvirt/libvirt.h>
17 #define POLL 5
19 struct list_el {
20 unsigned short keep;
21 unsigned int domainid;
22 float cost;
23 char *targetdomain;
24 virDomainPtr thisDomain;
25 AvahiEntryGroup *group;
28 virConnectPtr conn = NULL;
29 typedef struct list_el domu;
31 domu * domus = NULL; /* list of domains */
32 unsigned int domu_count = 0;
33 static AvahiSimplePoll *simple_poll = NULL;
35 static void resolve_callback(
36 AvahiServiceResolver *r,
37 AVAHI_GCC_UNUSED AvahiIfIndex interface,
38 AVAHI_GCC_UNUSED AvahiProtocol protocol,
39 AvahiResolverEvent event,
40 const char *name,
41 const char *type,
42 const char *domain,
43 const char *host_name,
44 const AvahiAddress *address,
45 uint16_t port,
46 AvahiStringList *txt,
47 AvahiLookupResultFlags flags,
48 void* userdata) {
50 assert(r);
52 /* Called whenever a service has been resolved successfully or timed out */
54 switch (event) {
55 case AVAHI_RESOLVER_FAILURE:
56 fprintf(stderr, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r))));
57 break;
59 case AVAHI_RESOLVER_FOUND: {
60 char a[AVAHI_ADDRESS_STR_MAX], *t;
61 AvahiStringList *needle;
63 fprintf(stderr, "Service '%s' of type '%s' in domain '%s':\n", name, type, domain);
65 avahi_address_snprint(a, sizeof(a), address);
66 t = avahi_string_list_to_string(txt);
67 fprintf(stderr,
68 "\t%s:%u (%s)\n"
69 "\tTXT=%s\n"
70 "\tcookie is %u\n"
71 "\tis_local: %i\n"
72 "\tour_own: %i\n"
73 "\twide_area: %i\n"
74 "\tmulticast: %i\n"
75 "\tcached: %i\n",
76 host_name, port, a,
78 avahi_string_list_get_service_cookie(txt),
79 !!(flags & AVAHI_LOOKUP_RESULT_LOCAL),
80 !!(flags & AVAHI_LOOKUP_RESULT_OUR_OWN),
81 !!(flags & AVAHI_LOOKUP_RESULT_WIDE_AREA),
82 !!(flags & AVAHI_LOOKUP_RESULT_MULTICAST),
83 !!(flags & AVAHI_LOOKUP_RESULT_CACHED));
85 avahi_free(t);
87 for (i = 0; i < domu_count; i++) {
88 const char *thisName = virDomainGetName(domu[i].thisDomain);
89 unsigned int len = strlen(thisName);
91 if (strlen(name) > len && name[len] == '.' && strncmp(thisName, name, len) == 0) {
92 if ((needle = avahi_string_list_find (txt, "cost")) != NULL) {
94 float amount;
95 char *cost;
96 avahi_string_list_get_pair (needle, NULL, &cost, NULL);
97 amount = atof(cost);
99 printf("%s: I'll run the VM for the competing cost of L$W %f\n", host_name, amount);
100 if (errno != ERANGE && amount < domu[i]->cost) {
101 domu[i]->cost = amount;
102 free(domu[i]->domain);
103 domu[i]->domain = strdup(&name[++len]);
106 avahi_free(cost);
108 break;
114 avahi_service_resolver_free(r);
117 static void browse_callback(
118 AvahiServiceBrowser *b,
119 AvahiIfIndex interface,
120 AvahiProtocol protocol,
121 AvahiBrowserEvent event,
122 const char *name,
123 const char *type,
124 const char *domain,
125 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
126 void* userdata) {
128 assert(b);
129 AvahiClient *c = avahi_service_browser_get_client(b);
131 /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
133 switch (event) {
134 case AVAHI_BROWSER_FAILURE:
136 fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))));
137 avahi_simple_poll_quit(simple_poll);
138 return;
140 case AVAHI_BROWSER_NEW:
141 fprintf(stderr, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
143 /* We ignore the returned resolver object. In the callback
144 function we free it. If the server is terminated before
145 the callback function is called the server will free
146 the resolver for us. */
148 if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, userdata)))
149 fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_client_errno(c)));
151 break;
153 case AVAHI_BROWSER_REMOVE:
154 fprintf(stderr, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
155 break;
157 case AVAHI_BROWSER_ALL_FOR_NOW:
158 case AVAHI_BROWSER_CACHE_EXHAUSTED:
159 fprintf(stderr, "(Browser) %s\n", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
160 break;
165 static void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
166 switch (state) {
167 case AVAHI_ENTRY_GROUP_ESTABLISHED :
168 /* The entry group has been established successfully */
169 break;
171 case AVAHI_ENTRY_GROUP_COLLISION : {
172 /* A service name collision with a remote service
173 * happened. */
174 break;
177 case AVAHI_ENTRY_GROUP_FAILURE :
178 /* Some kind of failure happened while we were registering our services */
179 avahi_simple_poll_quit(simple_poll);
180 break;
182 case AVAHI_ENTRY_GROUP_UNCOMMITED:
183 case AVAHI_ENTRY_GROUP_REGISTERING:
188 void create_services(AvahiClient *c) {
189 conn = virConnectOpenReadOnly(NULL);
190 if (conn == NULL)
191 return;
192 int maxid = virConnectNumOfDomains(conn);
193 if (maxid > 1) { /* ignore dom0 */
194 int *ids = (int *) malloc(sizeof(int) * maxid);
195 if ((maxid = virConnectListDomains(conn, &ids[0], maxid)) < 0) {
196 // error
197 } else {
198 int i;
199 unsigned int domu_count_new = (maxid - 1);
200 domu * domus_old = domus;
201 domus = (domu *) malloc(sizeof(domu) * domu_count_new);
202 for (i = 0; i < domu_count_new; i++) {
203 int j;
204 domus[i].group = NULL;
205 domus[i].domainid = ids[i+1];
206 domus[i].keep = 0;
208 for (j = 0; j < domu_count; j++) {
209 if (ids[i+1] == domus_old[j].domainid) {
210 domus[i].group = domus_old[j].group;
211 domus_old[j].keep = 1;
214 if (i > domu_count || domus[i].group == NULL) {
215 char txt[254];
216 domus[i].thisDomain = virDomainLookupByID(conn, ids[i+1]);
217 snprintf(txt, 254, "memory=%lu", virDomainGetMaxMemory(domus[i].thisDomain));
218 txt[253] = '\0'
219 domus[i].keep = 1;
220 domus[i].domain = NULL;
221 domus[i].cost = 100000.0f;
222 domus[i].group = avahi_entry_group_new(c, entry_group_callback, NULL);
223 avahi_entry_group_add_service(domus[i].group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0,
224 virDomainGetName(domus[i].thisDomain), "_offer._tcp", NULL, NULL, 651, txt, NULL, NULL);
225 avahi_entry_group_commit(domus[i].group);
229 for (i = 0; i < domu_count; i++) {
230 if (domus_old[i].keep == 0) {
231 if (domus_old[i].group)
232 avahi_entry_group_free(domus_old[i].group);
233 if (domus_old[i].domain)
234 free(domus_old[i].domain);
235 virDomainFree(domus[i].thisDomain);
236 } else if (domus_old[i].cost < 100000.0f) {
237 avahi_entry_group_free(domus_old[i].group);
238 domus_old[i].group = NULL;
239 printf("Migrate %s to %s\n", virDomainGetName(domus[i].thisDomain), domus[i].domain);
243 free(domus_old);
244 domu_count = domu_count_new;
246 free(ids);
248 virConnectClose(conn);
252 static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) {
253 assert(c);
255 /* Called whenever the client or server state changes */
257 switch (state) {
258 case AVAHI_CLIENT_S_RUNNING:
260 /* The server has startup successfully and registered its host
261 * name on the network, so it's time to create our services */
262 create_services(c);
263 break;
265 case AVAHI_CLIENT_FAILURE:
267 fprintf(stderr, "Client failure: %s\n", avahi_strerror(avahi_client_errno(c)));
268 avahi_simple_poll_quit(simple_poll);
270 break;
272 case AVAHI_CLIENT_S_COLLISION:
274 /* Let's drop our registered services. When the server is back
275 * in AVAHI_SERVER_RUNNING state we will register them
276 * again with the new host name. */
278 case AVAHI_CLIENT_S_REGISTERING:
280 /* The server records are now being established. This
281 * might be caused by a host name change. We need to wait
282 * for our own records to register until the host name is
283 * properly esatblished. */
285 /* if (group)
286 avahi_entry_group_reset(group);*/
288 break;
290 case AVAHI_CLIENT_CONNECTING:
297 static void modify_callback(AvahiTimeout *e, void *userdata) {
298 struct timeval tv;
299 AvahiClient *client = userdata;
301 // fprintf(stderr, "Doing some weird modification\n");
303 /* If the server is currently running, we need to remove our
304 * service and create it anew */
305 if (avahi_client_get_state(client) == AVAHI_CLIENT_S_RUNNING) {
306 /* And create them again with the new name */
307 create_services(client);
309 avahi_simple_poll_get(simple_poll)->timeout_update(e, avahi_elapse_time(&tv, 1000*POLL, 0));
314 int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
315 AvahiClient *client = NULL;
316 AvahiServiceBrowser *sb = NULL;
317 int error;
318 int ret = 1;
319 struct timeval tv;
320 /* Allocate main loop object */
321 if (!(simple_poll = avahi_simple_poll_new())) {
322 fprintf(stderr, "Failed to create simple poll object.\n");
323 goto fail;
326 /* Allocate a new client */
327 client = avahi_client_new(avahi_simple_poll_get(simple_poll), 0, client_callback, NULL, &error);
329 /* Check wether creating the client object succeeded */
330 if (!client) {
331 fprintf(stderr, "Failed to create client: %s\n", avahi_strerror(error));
332 goto fail;
335 /* Create the service browser */
336 if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_tender._tcp", NULL, 0, browse_callback, this))) {
337 fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(client)));
338 goto fail;
341 /* After POLL seconds we will update the list of VMs and hope we have some domains to migrate off to */
342 avahi_simple_poll_get(simple_poll)->timeout_new(
343 avahi_simple_poll_get(simple_poll),
344 avahi_elapse_time(&tv, 1000*POLL, 0),
345 modify_callback,
346 client);
348 /* Run the main loop */
349 avahi_simple_poll_loop(simple_poll);
351 ret = 0;
353 fail:
355 /* Cleanup things */
357 if (client)
358 avahi_client_free(client);
360 if (simple_poll)
361 avahi_simple_poll_free(simple_poll);
363 return ret;