1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdlib.h>
18#include <sys/socket.h>
19#include <netinet/in.h>
20#include <arpa/inet.h>
21
22#define LOG_TAG "PropertyManager"
23
24#include <cutils/log.h>
25
26#include "PropertyManager.h"
27
28PropertyManager::PropertyManager() {
29    mNamespaces = new PropertyNamespaceCollection();
30    pthread_mutex_init(&mLock, NULL);
31}
32
33PropertyManager::~PropertyManager() {
34    PropertyNamespaceCollection::iterator it;
35
36    for (it = mNamespaces->begin(); it != mNamespaces->end();) {
37        delete (*it);
38        it = mNamespaces->erase(it);
39    }
40    delete mNamespaces;
41}
42
43PropertyNamespace *PropertyManager::lookupNamespace_UNLOCKED(const char *ns) {
44    PropertyNamespaceCollection::iterator ns_it;
45
46    for (ns_it = mNamespaces->begin(); ns_it != mNamespaces->end(); ++ns_it) {
47        if (!strcasecmp(ns, (*ns_it)->getName()))
48            return (*ns_it);
49    }
50    errno = ENOENT;
51    return NULL;
52}
53
54Property *PropertyManager::lookupProperty_UNLOCKED(PropertyNamespace *ns, const char *name) {
55    PropertyCollection::iterator it;
56
57    for (it = ns->getProperties()->begin();
58         it != ns->getProperties()->end(); ++it) {
59        if (!strcasecmp(name, (*it)->getName()))
60            return (*it);
61    }
62    errno = ENOENT;
63    return NULL;
64}
65
66int PropertyManager::attachProperty(const char *ns_name, Property *p) {
67    PropertyNamespace *ns;
68
69    LOGD("Attaching property %s to namespace %s", p->getName(), ns_name);
70    pthread_mutex_lock(&mLock);
71    if (!(ns = lookupNamespace_UNLOCKED(ns_name))) {
72        LOGD("Creating namespace %s", ns_name);
73        ns = new PropertyNamespace(ns_name);
74        mNamespaces->push_back(ns);
75    }
76
77    if (lookupProperty_UNLOCKED(ns, p->getName())) {
78        errno = EADDRINUSE;
79        pthread_mutex_unlock(&mLock);
80        LOGE("Failed to register property %s.%s (%s)",
81            ns_name, p->getName(), strerror(errno));
82        return -1;
83    }
84
85    ns->getProperties()->push_back(p);
86    pthread_mutex_unlock(&mLock);
87    return 0;
88}
89
90int PropertyManager::detachProperty(const char *ns_name, Property *p) {
91    PropertyNamespace *ns;
92
93    LOGD("Detaching property %s from namespace %s", p->getName(), ns_name);
94    pthread_mutex_lock(&mLock);
95    if (!(ns = lookupNamespace_UNLOCKED(ns_name))) {
96        pthread_mutex_unlock(&mLock);
97        LOGE("Namespace '%s' not found", ns_name);
98        return -1;
99    }
100
101    PropertyCollection::iterator it;
102
103    for (it = ns->getProperties()->begin();
104         it != ns->getProperties()->end(); ++it) {
105        if (!strcasecmp(p->getName(), (*it)->getName())) {
106            delete ((*it));
107            ns->getProperties()->erase(it);
108            pthread_mutex_unlock(&mLock);
109            return 0;
110        }
111    }
112
113    LOGE("Property %s.%s not found", ns_name, p->getName());
114    pthread_mutex_unlock(&mLock);
115    errno = ENOENT;
116    return -1;
117}
118
119int PropertyManager::doSet(Property *p, int idx, const char *value) {
120
121    if (p->getReadOnly()) {
122        errno = EROFS;
123        return -1;
124    }
125
126    if (p->getType() == Property::Type_STRING) {
127        return p->set(idx, value);
128    } else if (p->getType() == Property::Type_INTEGER) {
129        int tmp;
130        errno = 0;
131        tmp = strtol(value, (char **) NULL, 10);
132        if (errno) {
133            LOGE("Failed to convert '%s' to int", value);
134            errno = EINVAL;
135            return -1;
136        }
137        return p->set(idx, tmp);
138    } else if (p->getType() == Property::Type_IPV4) {
139        struct in_addr tmp;
140        if (!inet_aton(value, &tmp)) {
141            LOGE("Failed to convert '%s' to ipv4", value);
142            errno = EINVAL;
143            return -1;
144        }
145        return p->set(idx, &tmp);
146    } else {
147        LOGE("Property '%s' has an unknown type (%d)", p->getName(),
148             p->getType());
149        errno = EINVAL;
150        return -1;
151    }
152    errno = ENOENT;
153    return -1;
154}
155
156int PropertyManager::doGet(Property *p, int idx, char *buffer, size_t max) {
157
158    if (p->getType() == Property::Type_STRING) {
159        if (p->get(idx, buffer, max)) {
160            LOGW("String property %s get failed (%s)", p->getName(),
161                 strerror(errno));
162            return -1;
163        }
164    }
165    else if (p->getType() == Property::Type_INTEGER) {
166        int tmp;
167        if (p->get(idx, &tmp)) {
168            LOGW("Integer property %s get failed (%s)", p->getName(),
169                 strerror(errno));
170            return -1;
171        }
172        snprintf(buffer, max, "%d", tmp);
173    } else if (p->getType() == Property::Type_IPV4) {
174        struct in_addr tmp;
175        if (p->get(idx, &tmp)) {
176            LOGW("IPV4 property %s get failed (%s)", p->getName(),
177                 strerror(errno));
178            return -1;
179        }
180        strncpy(buffer, inet_ntoa(tmp), max);
181    } else {
182        LOGE("Property '%s' has an unknown type (%d)", p->getName(),
183             p->getType());
184        errno = EINVAL;
185        return -1;
186    }
187    return 0;
188}
189
190/*
191 * IPropertyManager methods
192 */
193
194int PropertyManager::set(const char *name, const char *value) {
195
196    LOGD("set %s = '%s'", name, value);
197    pthread_mutex_lock(&mLock);
198    PropertyNamespaceCollection::iterator ns_it;
199    for (ns_it = mNamespaces->begin(); ns_it != mNamespaces->end(); ++ns_it) {
200        PropertyCollection::iterator p_it;
201        for (p_it = (*ns_it)->getProperties()->begin();
202             p_it != (*ns_it)->getProperties()->end(); ++p_it) {
203            for (int i = 0; i < (*p_it)->getNumElements(); i++) {
204                char fqn[255];
205                char tmp[8];
206                sprintf(tmp, "_%d", i);
207                snprintf(fqn, sizeof(fqn), "%s.%s%s",
208                         (*ns_it)->getName(), (*p_it)->getName(),
209                         ((*p_it)->getNumElements() > 1 ? tmp : ""));
210                if (!strcasecmp(name, fqn)) {
211                    pthread_mutex_unlock(&mLock);
212                    return doSet((*p_it), i, value);
213                }
214            }
215        }
216    }
217
218    LOGE("Property %s not found", name);
219    pthread_mutex_unlock(&mLock);
220    errno = ENOENT;
221    return -1;
222}
223
224const char *PropertyManager::get(const char *name, char *buffer, size_t max) {
225    pthread_mutex_lock(&mLock);
226    PropertyNamespaceCollection::iterator ns_it;
227    for (ns_it = mNamespaces->begin(); ns_it != mNamespaces->end(); ++ns_it) {
228        PropertyCollection::iterator p_it;
229        for (p_it = (*ns_it)->getProperties()->begin();
230             p_it != (*ns_it)->getProperties()->end(); ++p_it) {
231
232            for (int i = 0; i < (*p_it)->getNumElements(); i++) {
233                char fqn[255];
234                char tmp[8];
235                sprintf(tmp, "_%d", i);
236                snprintf(fqn, sizeof(fqn), "%s.%s%s",
237                         (*ns_it)->getName(), (*p_it)->getName(),
238                         ((*p_it)->getNumElements() > 1 ? tmp : ""));
239                if (!strcasecmp(name, fqn)) {
240                    pthread_mutex_unlock(&mLock);
241                    if (doGet((*p_it), i, buffer, max))
242                        return NULL;
243                    return buffer;
244                }
245            }
246        }
247    }
248
249    LOGE("Property %s not found", name);
250    pthread_mutex_unlock(&mLock);
251    errno = ENOENT;
252    return NULL;
253}
254
255android::List<char *> *PropertyManager::createPropertyList(const char *prefix) {
256    android::List<char *> *c = new android::List<char *>();
257
258    pthread_mutex_lock(&mLock);
259    PropertyNamespaceCollection::iterator ns_it;
260    for (ns_it = mNamespaces->begin(); ns_it != mNamespaces->end(); ++ns_it) {
261        PropertyCollection::iterator p_it;
262        for (p_it = (*ns_it)->getProperties()->begin();
263             p_it != (*ns_it)->getProperties()->end(); ++p_it) {
264            for (int i = 0; i < (*p_it)->getNumElements(); i++) {
265                char fqn[255];
266                char tmp[8];
267                sprintf(tmp, "_%d", i);
268                snprintf(fqn, sizeof(fqn), "%s.%s%s",
269                         (*ns_it)->getName(), (*p_it)->getName(),
270                         ((*p_it)->getNumElements() > 1 ? tmp : ""));
271                if (!prefix ||
272                    (prefix && !strncasecmp(fqn, prefix, strlen(prefix)))) {
273                    c->push_back(strdup(fqn));
274                }
275            }
276        }
277    }
278    pthread_mutex_unlock(&mLock);
279    return c;
280}
281