1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <errno.h>
4 
5 #include "sd-netlink.h"
6 
7 #include "alloc-util.h"
8 #include "netlink-internal.h"
9 #include "netlink-slot.h"
10 #include "string-util.h"
11 
netlink_slot_allocate(sd_netlink * nl,bool floating,NetlinkSlotType type,size_t extra,void * userdata,const char * description,sd_netlink_slot ** ret)12 int netlink_slot_allocate(
13                 sd_netlink *nl,
14                 bool floating,
15                 NetlinkSlotType type,
16                 size_t extra,
17                 void *userdata,
18                 const char *description,
19                 sd_netlink_slot **ret) {
20 
21         _cleanup_free_ sd_netlink_slot *slot = NULL;
22 
23         assert(nl);
24         assert(ret);
25 
26         slot = malloc0(offsetof(sd_netlink_slot, reply_callback) + extra);
27         if (!slot)
28                 return -ENOMEM;
29 
30         slot->n_ref = 1;
31         slot->netlink = nl;
32         slot->userdata = userdata;
33         slot->type = type;
34         slot->floating = floating;
35 
36         if (description) {
37                 slot->description = strdup(description);
38                 if (!slot->description)
39                         return -ENOMEM;
40         }
41 
42         if (!floating)
43                 sd_netlink_ref(nl);
44 
45         LIST_PREPEND(slots, nl->slots, slot);
46 
47         *ret = TAKE_PTR(slot);
48 
49         return 0;
50 }
51 
netlink_slot_disconnect(sd_netlink_slot * slot,bool unref)52 void netlink_slot_disconnect(sd_netlink_slot *slot, bool unref) {
53         sd_netlink *nl;
54 
55         assert(slot);
56 
57         nl = slot->netlink;
58         if (!nl)
59                 return;
60 
61         switch (slot->type) {
62 
63         case NETLINK_REPLY_CALLBACK:
64                 (void) hashmap_remove(nl->reply_callbacks, &slot->reply_callback.serial);
65 
66                 if (slot->reply_callback.timeout != 0)
67                         prioq_remove(nl->reply_callbacks_prioq, &slot->reply_callback, &slot->reply_callback.prioq_idx);
68 
69                 break;
70         case NETLINK_MATCH_CALLBACK:
71                 LIST_REMOVE(match_callbacks, nl->match_callbacks, &slot->match_callback);
72 
73                 for (size_t i = 0; i < slot->match_callback.n_groups; i++)
74                         (void) socket_broadcast_group_unref(nl, slot->match_callback.groups[i]);
75 
76                 slot->match_callback.n_groups = 0;
77                 slot->match_callback.groups = mfree(slot->match_callback.groups);
78 
79                 break;
80         default:
81                 assert_not_reached();
82         }
83 
84         slot->type = _NETLINK_SLOT_INVALID;
85         slot->netlink = NULL;
86         LIST_REMOVE(slots, nl->slots, slot);
87 
88         if (!slot->floating)
89                 sd_netlink_unref(nl);
90         else if (unref)
91                 sd_netlink_slot_unref(slot);
92 }
93 
netlink_slot_free(sd_netlink_slot * slot)94 static sd_netlink_slot* netlink_slot_free(sd_netlink_slot *slot) {
95         assert(slot);
96 
97         netlink_slot_disconnect(slot, false);
98 
99         if (slot->destroy_callback)
100                 slot->destroy_callback(slot->userdata);
101 
102         free(slot->description);
103         return mfree(slot);
104 }
105 
106 DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_netlink_slot, sd_netlink_slot, netlink_slot_free);
107 
sd_netlink_slot_get_netlink(sd_netlink_slot * slot)108 sd_netlink *sd_netlink_slot_get_netlink(sd_netlink_slot *slot) {
109         assert_return(slot, NULL);
110 
111         return slot->netlink;
112 }
113 
sd_netlink_slot_get_userdata(sd_netlink_slot * slot)114 void *sd_netlink_slot_get_userdata(sd_netlink_slot *slot) {
115         assert_return(slot, NULL);
116 
117         return slot->userdata;
118 }
119 
sd_netlink_slot_set_userdata(sd_netlink_slot * slot,void * userdata)120 void *sd_netlink_slot_set_userdata(sd_netlink_slot *slot, void *userdata) {
121         void *ret;
122 
123         assert_return(slot, NULL);
124 
125         ret = slot->userdata;
126         slot->userdata = userdata;
127 
128         return ret;
129 }
130 
sd_netlink_slot_get_destroy_callback(sd_netlink_slot * slot,sd_netlink_destroy_t * callback)131 int sd_netlink_slot_get_destroy_callback(sd_netlink_slot *slot, sd_netlink_destroy_t *callback) {
132         assert_return(slot, -EINVAL);
133 
134         if (callback)
135                 *callback = slot->destroy_callback;
136 
137         return !!slot->destroy_callback;
138 }
139 
sd_netlink_slot_set_destroy_callback(sd_netlink_slot * slot,sd_netlink_destroy_t callback)140 int sd_netlink_slot_set_destroy_callback(sd_netlink_slot *slot, sd_netlink_destroy_t callback) {
141         assert_return(slot, -EINVAL);
142 
143         slot->destroy_callback = callback;
144         return 0;
145 }
146 
sd_netlink_slot_get_floating(sd_netlink_slot * slot)147 int sd_netlink_slot_get_floating(sd_netlink_slot *slot) {
148         assert_return(slot, -EINVAL);
149 
150         return slot->floating;
151 }
152 
sd_netlink_slot_set_floating(sd_netlink_slot * slot,int b)153 int sd_netlink_slot_set_floating(sd_netlink_slot *slot, int b) {
154         assert_return(slot, -EINVAL);
155 
156         if (slot->floating == !!b)
157                 return 0;
158 
159         if (!slot->netlink) /* Already disconnected */
160                 return -ESTALE;
161 
162         slot->floating = b;
163 
164         if (b) {
165                 sd_netlink_slot_ref(slot);
166                 sd_netlink_unref(slot->netlink);
167         } else {
168                 sd_netlink_ref(slot->netlink);
169                 sd_netlink_slot_unref(slot);
170         }
171 
172         return 1;
173 }
174 
sd_netlink_slot_get_description(sd_netlink_slot * slot,const char ** description)175 int sd_netlink_slot_get_description(sd_netlink_slot *slot, const char **description) {
176         assert_return(slot, -EINVAL);
177 
178         if (description)
179                 *description = slot->description;
180 
181         return !!slot->description;
182 }
183 
sd_netlink_slot_set_description(sd_netlink_slot * slot,const char * description)184 int sd_netlink_slot_set_description(sd_netlink_slot *slot, const char *description) {
185         assert_return(slot, -EINVAL);
186 
187         return free_and_strdup(&slot->description, description);
188 }
189