add some debug messages for device user count
[project/netifd.git] / alias.c
1 /*
2  * netifd - network interface daemon
3  * Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2
7  * as published by the Free Software Foundation
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14 #include <string.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17
18 #include "netifd.h"
19 #include "device.h"
20
21 static struct avl_tree aliases;
22
23 struct alias_device {
24         struct avl_node avl;
25         struct device dev;
26         struct device_user dep;
27         bool cleanup;
28         char name[];
29 };
30
31 static const struct device_type alias_device_type;
32
33 static int
34 alias_device_set_state(struct device *dev, bool state)
35 {
36         struct alias_device *alias;
37
38         alias = container_of(dev, struct alias_device, dev);
39         if (!alias->dep.dev)
40                 return -1;
41
42         if (state)
43                 return device_claim(&alias->dep);
44
45         device_release(&alias->dep);
46         if (alias->cleanup)
47                 device_remove_user(&alias->dep);
48         return 0;
49 }
50
51 static void alias_device_cb(struct device_user *dep, enum device_event ev)
52 {
53         struct alias_device *alias;
54         bool present = false;
55
56         alias = container_of(dep, struct alias_device, dep);
57         switch (ev) {
58         case DEV_EVENT_ADD:
59                 present = true;
60         case DEV_EVENT_REMOVE:
61                 device_set_present(&alias->dev, present);
62                 break;
63         default:
64                 device_broadcast_event(&alias->dev, ev);
65                 break;
66         }
67 }
68
69 static struct device *
70 alias_device_create(const char *name, struct blob_attr *attr)
71 {
72         struct alias_device *alias;
73
74         alias = calloc(1, sizeof(*alias) + strlen(name) + 1);
75         strcpy(alias->name, name);
76         alias->dev.set_state = alias_device_set_state;
77         alias->dev.hidden = true;
78         device_init_virtual(&alias->dev, &alias_device_type, NULL);
79         alias->avl.key = alias->name;
80         avl_insert(&aliases, &alias->avl);
81         alias->dep.alias = true;
82         alias->dep.cb = alias_device_cb;
83
84         return &alias->dev;
85 }
86
87 static void alias_device_free(struct device *dev)
88 {
89         struct alias_device *alias;
90
91         alias = container_of(dev, struct alias_device, dev);
92         avl_delete(&aliases, &alias->avl);
93         free(alias);
94 }
95
96 static const struct device_type alias_device_type = {
97         .name = "Network alias",
98         .create = alias_device_create,
99         .free = alias_device_free,
100 };
101
102 void
103 alias_notify_device(const char *name, struct device *dev)
104 {
105         struct alias_device *alias;
106
107         device_lock();
108
109         alias = avl_find_element(&aliases, name, alias, avl);
110         if (!alias)
111                 return;
112
113         alias->cleanup = !dev;
114         if (dev) {
115                 if (dev != alias->dep.dev) {
116                         device_remove_user(&alias->dep);
117                         strcpy(alias->dev.ifname, dev->ifname);
118                         device_add_user(&alias->dep, dev);
119                         alias->dev.hidden = false;
120                         device_broadcast_event(&alias->dev, DEV_EVENT_UPDATE_IFNAME);
121                 }
122         }
123
124         if (!dev && alias->dep.dev && !alias->dep.dev->active) {
125                 device_remove_user(&alias->dep);
126                 alias->dev.hidden = true;
127                 alias->dev.ifname[0] = 0;
128                 device_broadcast_event(&alias->dev, DEV_EVENT_UPDATE_IFNAME);
129         }
130
131         device_unlock();
132 }
133
134 struct device *
135 device_alias_get(const char *name)
136 {
137         struct alias_device *alias;
138
139         alias = avl_find_element(&aliases, name, alias, avl);
140         if (alias)
141                 return &alias->dev;
142
143         return alias_device_create(name, NULL);
144 }
145
146 static void __init alias_init(void)
147 {
148         avl_init(&aliases, avl_strcmp, false, NULL);
149 }