8 #include <avahi-client/client.h>
9 #include <avahi-client/publish.h>
10 #include <avahi-client/lookup.h>
12 #include <avahi-common/alternative.h>
13 #include <avahi-common/simple-watch.h>
14 #include <avahi-common/malloc.h>
15 #include <avahi-common/error.h>
16 #include <avahi-common/timeval.h>
18 #include <libvirt/libvirt.h>
24 unsigned int domainid
;
27 virDomainPtr thisDomain
; /* TODO: GAAT DIT GOED? */
28 AvahiEntryGroup
*group
;
31 virConnectPtr conn
= NULL
;
32 typedef struct list_el domu
;
34 domu
* domus
= NULL
; /* list of domains */
35 unsigned int domu_count
= 0;
36 static AvahiSimplePoll
*simple_poll
= NULL
;
38 static void resolve_callback(
39 AvahiServiceResolver
*r
,
40 AVAHI_GCC_UNUSED AvahiIfIndex interface
,
41 AVAHI_GCC_UNUSED AvahiProtocol protocol
,
42 AvahiResolverEvent event
,
46 const char *host_name
,
47 const AvahiAddress
*address
,
50 AvahiLookupResultFlags flags
,
55 /* Called whenever a service has been resolved successfully or timed out */
58 case AVAHI_RESOLVER_FAILURE
:
59 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
))));
62 case AVAHI_RESOLVER_FOUND
: {
63 char a
[AVAHI_ADDRESS_STR_MAX
], *t
;
64 AvahiStringList
*needle
;
66 fprintf(stderr
, "(Resolver) Service '%s' of type '%s' in domain '%s':\n", name
, type
, domain
);
68 avahi_address_snprint(a
, sizeof(a
), address
);
69 t
= avahi_string_list_to_string(txt
);
81 avahi_string_list_get_service_cookie(txt
),
82 !!(flags
& AVAHI_LOOKUP_RESULT_LOCAL
),
83 !!(flags
& AVAHI_LOOKUP_RESULT_OUR_OWN
),
84 !!(flags
& AVAHI_LOOKUP_RESULT_WIDE_AREA
),
85 !!(flags
& AVAHI_LOOKUP_RESULT_MULTICAST
),
86 !!(flags
& AVAHI_LOOKUP_RESULT_CACHED
));
91 for (i
= 0; i
< domu_count
; i
++) {
92 const char *thisName
= virDomainGetName(domus
[i
].thisDomain
);
93 unsigned int len
= strlen(thisName
);
95 if (strlen(name
) > len
&& name
[len
] == '.' && strncmp(thisName
, name
, len
) == 0) {
96 if ((needle
= avahi_string_list_find (txt
, "cost")) != NULL
) {
100 avahi_string_list_get_pair (needle
, NULL
, &cost
, NULL
);
103 printf("%s: I'll run %s for the competing cost of L$W %f\n", host_name
, thisName
, amount
);
104 if (errno
!= ERANGE
&& amount
< domus
[i
].cost
) {
105 domus
[i
].cost
= amount
;
106 if (domus
[i
].targetdomain
)
107 free(domus
[i
].targetdomain
);
108 domus
[i
].targetdomain
= strdup(&name
[++len
]);
118 avahi_service_resolver_free(r
);
121 static void browse_callback(
122 AvahiServiceBrowser
*b
,
123 AvahiIfIndex interface
,
124 AvahiProtocol protocol
,
125 AvahiBrowserEvent event
,
129 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags
,
133 AvahiClient
*c
= avahi_service_browser_get_client(b
);
135 /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
138 case AVAHI_BROWSER_FAILURE
:
140 fprintf(stderr
, "(Browser) %s\n", avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b
))));
141 avahi_simple_poll_quit(simple_poll
);
144 case AVAHI_BROWSER_NEW
:
145 fprintf(stderr
, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name
, type
, domain
);
147 /* We ignore the returned resolver object. In the callback
148 function we free it. If the server is terminated before
149 the callback function is called the server will free
150 the resolver for us. */
152 if (!(avahi_service_resolver_new(c
, interface
, protocol
, name
, type
, domain
, AVAHI_PROTO_UNSPEC
, 0, resolve_callback
, userdata
)))
153 fprintf(stderr
, "Failed to resolve service '%s': %s\n", name
, avahi_strerror(avahi_client_errno(c
)));
157 case AVAHI_BROWSER_REMOVE
:
158 fprintf(stderr
, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name
, type
, domain
);
161 case AVAHI_BROWSER_ALL_FOR_NOW
:
162 case AVAHI_BROWSER_CACHE_EXHAUSTED
:
163 fprintf(stderr
, "(Browser) %s\n", event
== AVAHI_BROWSER_CACHE_EXHAUSTED
? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
169 static void entry_group_callback(AvahiEntryGroup
*g
, AvahiEntryGroupState state
, AVAHI_GCC_UNUSED
void *userdata
) {
171 case AVAHI_ENTRY_GROUP_ESTABLISHED
:
172 /* The entry group has been established successfully */
175 case AVAHI_ENTRY_GROUP_COLLISION
: {
176 /* A service name collision with a remote service
181 case AVAHI_ENTRY_GROUP_FAILURE
:
182 /* Some kind of failure happened while we were registering our services */
183 avahi_simple_poll_quit(simple_poll
);
186 case AVAHI_ENTRY_GROUP_UNCOMMITED
:
187 case AVAHI_ENTRY_GROUP_REGISTERING
:
192 void create_services(AvahiClient
*c
) {
193 conn
= virConnectOpen(NULL
);
196 int maxid
= virConnectNumOfDomains(conn
);
197 if (maxid
> 1) { /* ignore dom0 */
198 int *ids
= (int *) malloc(sizeof(int) * maxid
);
199 if ((maxid
= virConnectListDomains(conn
, &ids
[0], maxid
)) < 0) {
203 unsigned int domu_count_new
= (maxid
- 1);
204 domu
* domus_old
= domus
;
205 domus
= (domu
*) malloc(sizeof(domu
) * domu_count_new
);
206 for (i
= 0; i
< domu_count_new
; i
++) {
208 domus
[i
].thisDomain
= NULL
;
209 domus
[i
].targetdomain
= NULL
;
210 domus
[i
].group
= NULL
;
211 domus
[i
].domainid
= ids
[i
+1];
213 domus
[i
].cost
= 100000.0f
;
215 for (j
= 0; j
< domu_count
; j
++) {
216 if (ids
[i
+1] == domus_old
[j
].domainid
) {
217 domus
[i
].group
= domus_old
[j
].group
;
218 domus_old
[j
].keep
= 1;
221 if (i
> domu_count
|| domus
[i
].group
== NULL
) {
223 domus
[i
].thisDomain
= virDomainLookupByID(conn
, ids
[i
+1]);
224 snprintf(txt
, 254, "memory=%lu", virDomainGetMaxMemory(domus
[i
].thisDomain
));
227 domus
[i
].group
= avahi_entry_group_new(c
, entry_group_callback
, NULL
);
228 avahi_entry_group_add_service(domus
[i
].group
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, 0,
229 virDomainGetName(domus
[i
].thisDomain
), "_offer._tcp", NULL
, NULL
, 651, txt
, NULL
, NULL
);
230 avahi_entry_group_commit(domus
[i
].group
);
234 for (i
= 0; i
< domu_count
; i
++) {
235 if (domus_old
[i
].keep
== 0) {
236 if (domus_old
[i
].group
)
237 avahi_entry_group_free(domus_old
[i
].group
);
238 if (domus_old
[i
].targetdomain
)
239 free(domus_old
[i
].targetdomain
);
240 virDomainFree(domus
[i
].thisDomain
);
241 } else if (domus_old
[i
].thisDomain
&& domus_old
[i
].cost
< 100000.0f
) {
243 virDomainPtr ddomain
;
245 avahi_entry_group_free(domus_old
[i
].group
);
246 snprintf(uri
, 126, "xen://%s/", domus_old
[i
].targetdomain
);
248 dconn
= virConnectOpen(uri
);
249 domus_old
[i
].group
= NULL
;
250 if ((ddomain
= virDomainMigrate(domus_old
[i
].thisDomain
, dconn
, VIR_MIGRATE_LIVE
, NULL
, NULL
, 0)) == NULL
)
251 printf("Migration failed!");
253 printf("Migrated %s to %s\n", virDomainGetName(domus_old
[i
].thisDomain
), domus_old
[i
].targetdomain
);
254 virDomainFree(ddomain
);
255 virConnectClose(conn
);
261 domu_count
= domu_count_new
;
265 virConnectClose(conn
);
269 static void client_callback(AvahiClient
*c
, AvahiClientState state
, AVAHI_GCC_UNUSED
void * userdata
) {
272 /* Called whenever the client or server state changes */
275 case AVAHI_CLIENT_S_RUNNING
:
277 /* The server has startup successfully and registered its host
278 * name on the network, so it's time to create our services */
282 case AVAHI_CLIENT_FAILURE
:
284 fprintf(stderr
, "Client failure: %s\n", avahi_strerror(avahi_client_errno(c
)));
285 avahi_simple_poll_quit(simple_poll
);
289 case AVAHI_CLIENT_S_COLLISION
:
291 /* Let's drop our registered services. When the server is back
292 * in AVAHI_SERVER_RUNNING state we will register them
293 * again with the new host name. */
295 case AVAHI_CLIENT_S_REGISTERING
:
297 /* The server records are now being established. This
298 * might be caused by a host name change. We need to wait
299 * for our own records to register until the host name is
300 * properly esatblished. */
303 avahi_entry_group_reset(group);*/
307 case AVAHI_CLIENT_CONNECTING
:
314 static void modify_callback(AvahiTimeout
*e
, void *userdata
) {
316 AvahiClient
*client
= userdata
;
318 // fprintf(stderr, "Doing some weird modification\n");
320 /* If the server is currently running, we need to remove our
321 * service and create it anew */
322 if (avahi_client_get_state(client
) == AVAHI_CLIENT_S_RUNNING
) {
323 /* And create them again with the new name */
324 create_services(client
);
326 avahi_simple_poll_get(simple_poll
)->timeout_update(e
, avahi_elapse_time(&tv
, 1000*POLL
, 0));
331 int main(AVAHI_GCC_UNUSED
int argc
, AVAHI_GCC_UNUSED
char*argv
[]) {
332 AvahiClient
*client
= NULL
;
333 AvahiServiceBrowser
*sb
= NULL
;
337 /* Allocate main loop object */
338 if (!(simple_poll
= avahi_simple_poll_new())) {
339 fprintf(stderr
, "Failed to create simple poll object.\n");
343 /* Allocate a new client */
344 client
= avahi_client_new(avahi_simple_poll_get(simple_poll
), 0, client_callback
, NULL
, &error
);
346 /* Check wether creating the client object succeeded */
348 fprintf(stderr
, "Failed to create client: %s\n", avahi_strerror(error
));
352 /* Create the service browser */
353 if (!(sb
= avahi_service_browser_new(client
, AVAHI_IF_UNSPEC
, AVAHI_PROTO_UNSPEC
, "_tender._tcp", NULL
, 0, browse_callback
, NULL
))) {
354 fprintf(stderr
, "Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(client
)));
358 /* After POLL seconds we will update the list of VMs and hope we have some domains to migrate off to */
359 avahi_simple_poll_get(simple_poll
)->timeout_new(
360 avahi_simple_poll_get(simple_poll
),
361 avahi_elapse_time(&tv
, 1000*POLL
, 0),
365 /* Run the main loop */
366 avahi_simple_poll_loop(simple_poll
);
375 avahi_client_free(client
);
378 avahi_simple_poll_free(simple_poll
);