2004-04-22 Marcus Brinkmann <marcus@g10code.de>
[libgcrypt.git] / src / module.c
1 /* module.c - Module management for libgcrypt.
2  * Copyright (C) 2003 Free Software Foundation, Inc.
3  *
4  * This file is part of Libgcrypt.
5  *
6  * Libgcrypt is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser general Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * Libgcrypt is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <assert.h>
22 #include <config.h>
23 #include <errno.h>
24 #include "g10lib.h"
25
26 #define MODULE_ID_MIN 600
27
28 /* Internal function.  Generate a new, unique module ID for a module
29    that should be inserted into the module chain starting at
30    MODULES.  */
31 static gcry_err_code_t
32 _gcry_module_id_new (gcry_module_t modules, unsigned int *id_new)
33 {
34   /* FIXME, what should be the ID of the first module registered by
35      the user?  */
36   unsigned int id_min = MODULE_ID_MIN, id_max = (unsigned int) -1, mod_id;
37   gcry_err_code_t err = GPG_ERR_NO_ERROR;
38   gcry_module_t module;
39
40   /* Search for unused ID.  */
41   for (mod_id = id_min; mod_id < id_max; mod_id++)
42     {
43       /* Search for a module with the current ID.  */
44       for (module = modules; module; module = module->next)
45         if (mod_id == module->mod_id)
46           break;
47
48       if (! module)
49         /* None found -> the ID is available for use.  */
50         break;
51     }
52
53   if (mod_id < id_max)
54     /* Done.  */
55     *id_new = mod_id;
56   else
57     /* No free ID found.  */
58     err = GPG_ERR_INTERNAL;
59
60   return err;
61 }
62
63 /* Add a module specification to the list ENTRIES.  The new module has
64    it's use-counter set to one.  */
65 gcry_err_code_t
66 _gcry_module_add (gcry_module_t *entries, unsigned int mod_id,
67                   void *spec, gcry_module_t *module)
68 {
69   gcry_err_code_t err = 0;
70   gcry_module_t entry;
71
72   if (! mod_id)
73     err = _gcry_module_id_new (*entries, &mod_id);
74
75   if (! err)
76     {
77       entry = gcry_malloc (sizeof (struct gcry_module));
78       if (! entry)
79         err = gpg_err_code_from_errno (errno);
80     }
81
82   if (! err)
83     {
84       /* Fill new module entry.  */
85       entry->flags = 0;
86       entry->counter = 1;
87       entry->spec = spec;
88       entry->mod_id = mod_id;
89
90       /* Link it into the list.  */
91       entry->next = *entries;
92       entry->prevp = entries;
93       if (*entries)
94         (*entries)->prevp = &entry->next;
95       *entries = entry;
96
97       /* And give it to the caller.  */
98       if (module)
99         *module = entry;
100     }
101   return err;
102 }
103
104 /* Internal function.  Unlink CIPHER_ENTRY from the list of registered
105    ciphers and destroy it.  */
106 static void
107 _gcry_module_drop (gcry_module_t entry)
108 {
109   *entry->prevp = entry->next;
110   if (entry->next)
111     entry->next->prevp = entry->prevp;
112
113   gcry_free (entry);
114 }
115
116 /* Lookup a module specification by it's ID.  After a successfull
117    lookup, the module has it's resource counter incremented.  */
118 gcry_module_t 
119 _gcry_module_lookup_id (gcry_module_t entries, unsigned int mod_id)
120 {
121   gcry_module_t entry;
122
123   for (entry = entries; entry; entry = entry->next)
124     if (entry->mod_id == mod_id)
125       {
126         entry->counter++;
127         break;
128       }
129
130   return entry;
131 }
132
133 /* Lookup a module specification.  After a successfull lookup, the
134    module has it's resource counter incremented.  FUNC is a function
135    provided by the caller, which is responsible for identifying the
136    wanted module.  */
137 gcry_module_t 
138 _gcry_module_lookup (gcry_module_t entries, void *data,
139                      gcry_module_lookup_t func)
140 {
141   gcry_module_t entry;
142
143   for (entry = entries; entry; entry = entry->next)
144     if ((*func) (entry->spec, data))
145       {
146         entry->counter++;
147         break;
148       }
149
150   return entry;
151 }
152
153 /* Release a module.  In case the use-counter reaches zero, destroy
154    the module.  Passing MODULE as NULL is a dummy operation (similar
155    to free()). */
156 void
157 _gcry_module_release (gcry_module_t module)
158 {
159   if (module && ! --module->counter)
160     _gcry_module_drop (module);
161 }
162
163 /* Add a reference to a module.  */
164 void
165 _gcry_module_use (gcry_module_t module)
166 {
167   ++module->counter;
168 }
169
170 /* If LIST is zero, write the number of modules identified by MODULES
171    to LIST_LENGTH and return.  If LIST is non-zero, the first
172    *LIST_LENGTH algorithm IDs are stored in LIST, which must be of
173    according size.  In case there are less cipher modules than
174    *LIST_LENGTH, *LIST_LENGTH is updated to the correct number.  */
175 gcry_err_code_t
176 _gcry_module_list (gcry_module_t modules,
177                    int *list, int *list_length)
178 {
179   gcry_err_code_t err = GPG_ERR_NO_ERROR;
180   gcry_module_t module;
181   int length, i;
182
183   for (module = modules, length = 0; module; module = module->next, length++);
184
185   if (list)
186     {
187       if (length > *list_length)
188         length = *list_length;
189
190       for (module = modules, i = 0; i < length; module = module->next, i++)
191         list[i] = module->mod_id;
192
193       if (length < *list_length)
194         *list_length = length;
195     }
196   else
197     *list_length = length;
198
199   return err;
200 }