proc-map.c (7267B)
1/* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20#include "config.h" 21#include "common/list.h" 22#include "proc.h" 23#include "proc-map.h" 24 25#include <guacamole/client.h> 26#include <guacamole/mem.h> 27 28#include <stdlib.h> 29#include <string.h> 30 31/** 32 * A value to be stored in the buckets, containing the guacd proc itself, 33 * as well as a link to the element in the list of all guacd processes. 34 */ 35typedef struct guacd_proc_map_entry { 36 37 /** 38 * The guacd process itself. 39 */ 40 guacd_proc* proc; 41 42 /** 43 * A pointer to the corresponding entry in the list of all processes. 44 */ 45 guac_common_list_element* element; 46 47} guacd_proc_map_entry; 48 49/** 50 * Returns a hash code based on the given connection ID. 51 * 52 * @param str 53 * The string containing the connection ID. 54 * 55 * @return 56 * A reasonably well-distributed hash code for the given string. 57 */ 58static unsigned int __guacd_client_hash(const char* str) { 59 60 unsigned int hash_value = 0; 61 int c; 62 63 /* Apply each character in string to the hash code */ 64 while ((c = *(str++))) 65 hash_value = hash_value * 65599 + c; 66 67 return hash_value; 68 69} 70 71/** 72 * Locates the bucket corresponding to the hash code indicated by the given id, 73 * where the hash code is dictated by __guacd_client_hash(). Each bucket is an 74 * instance of guac_common_list. 75 * 76 * @param map 77 * The map to retrieve the hash bucket from. 78 * 79 * @param id 80 * The ID whose hash code determines the bucket being retrieved. 81 * 82 * @return 83 * The bucket corresponding to the hash code for the given ID, represented 84 * by a guac_common_list. 85 */ 86static guac_common_list* __guacd_proc_find_bucket(guacd_proc_map* map, 87 const char* id) { 88 89 const int index = __guacd_client_hash(id) % GUACD_PROC_MAP_BUCKETS; 90 return map->__buckets[index]; 91 92} 93 94/** 95 * Given a bucket of guacd_proc instances, returns the guacd_proc having the 96 * guac_client with the given ID, or NULL if no such client is stored. 97 * 98 * @param bucket 99 * The bucket of guacd_proc instances to search, represented as a 100 * guac_common_list. 101 * 102 * @param id 103 * The ID of the guac_client whose corresponding guacd_proc instance should 104 * be located within the bucket. 105 * 106 * @return 107 * The guac_common_list_element containing the guacd_proc instance 108 * corresponding to the guac_client having the given ID, or NULL of no such 109 * element exists. 110 */ 111static guac_common_list_element* __guacd_proc_find(guac_common_list* bucket, 112 const char* id) { 113 114 guac_common_list_element* current = bucket->head; 115 116 /* Search for matching element within bucket */ 117 while (current != NULL) { 118 119 /* Check connection ID */ 120 guacd_proc* proc = ((guacd_proc_map_entry*) current->data)->proc; 121 if (strcmp(proc->client->connection_id, id) == 0) 122 break; 123 124 current = current->next; 125 } 126 127 return current; 128 129} 130 131guacd_proc_map* guacd_proc_map_alloc() { 132 133 guacd_proc_map* map = guac_mem_alloc(sizeof(guacd_proc_map)); 134 map->processes = guac_common_list_alloc(); 135 guac_common_list** current; 136 137 int i; 138 139 /* Init all buckets */ 140 current = map->__buckets; 141 142 for (i=0; i<GUACD_PROC_MAP_BUCKETS; i++) { 143 *current = guac_common_list_alloc(); 144 current++; 145 } 146 147 return map; 148 149} 150 151int guacd_proc_map_add(guacd_proc_map* map, guacd_proc* proc) { 152 153 const char* identifier = proc->client->connection_id; 154 guac_common_list* bucket = __guacd_proc_find_bucket(map, identifier); 155 guac_common_list_element* found; 156 157 /* Retrieve corresponding element, if any */ 158 guac_common_list_lock(bucket); 159 found = __guacd_proc_find(bucket, identifier); 160 161 /* If no such element, we can add the new client successfully */ 162 if (found == NULL) { 163 164 guacd_proc_map_entry* entry = guac_mem_alloc(sizeof(guacd_proc_map_entry)); 165 166 guac_common_list_lock(map->processes); 167 entry->element = guac_common_list_add(map->processes, proc); 168 guac_common_list_unlock(map->processes); 169 170 entry->proc = proc; 171 172 guac_common_list_add(bucket, entry); 173 guac_common_list_unlock(bucket); 174 175 return 0; 176 } 177 178 /* Otherwise, fail - already exists */ 179 guac_common_list_unlock(bucket); 180 return 1; 181 182} 183 184guacd_proc* guacd_proc_map_retrieve(guacd_proc_map* map, const char* id) { 185 186 guacd_proc* proc; 187 188 guac_common_list* bucket = __guacd_proc_find_bucket(map, id); 189 guac_common_list_element* found; 190 191 /* Retrieve corresponding element, if any */ 192 guac_common_list_lock(bucket); 193 found = __guacd_proc_find(bucket, id); 194 195 /* If no such element, fail */ 196 if (found == NULL) { 197 guac_common_list_unlock(bucket); 198 return NULL; 199 } 200 201 proc = ((guacd_proc_map_entry*) found->data)->proc; 202 203 guac_common_list_unlock(bucket); 204 return proc; 205 206} 207 208guacd_proc* guacd_proc_map_remove(guacd_proc_map* map, const char* id) { 209 210 guacd_proc* proc; 211 212 guac_common_list* bucket = __guacd_proc_find_bucket(map, id); 213 guac_common_list_element* found; 214 215 /* Retrieve corresponding element, if any */ 216 guac_common_list_lock(bucket); 217 found = __guacd_proc_find(bucket, id); 218 219 /* If no such element, fail */ 220 if (found == NULL) { 221 guac_common_list_unlock(bucket); 222 return NULL; 223 } 224 225 guacd_proc_map_entry* entry = (guacd_proc_map_entry*) found->data; 226 227 /* Find and remove the key from the process list */ 228 guac_common_list_lock(map->processes); 229 guac_common_list_remove(map->processes, entry->element); 230 guac_common_list_unlock(map->processes); 231 232 proc = entry->proc; 233 guac_common_list_remove(bucket, found); 234 235 free (entry); 236 237 guac_common_list_unlock(bucket); 238 return proc; 239 240} 241 242void guacd_proc_map_foreach(guacd_proc_map* map, 243 guacd_proc_map_foreach_callback* callback, void* data) { 244 245 guac_common_list* list = map->processes; 246 247 guac_common_list_lock(list); 248 249 /* Invoke the callback for every element in the list */ 250 guac_common_list_element* element; 251 for (element = list->head; element != NULL; element = element->next) 252 callback((guacd_proc*) element->data, data); 253 254 guac_common_list_unlock(list); 255 256} 257 258void guacd_proc_map_free(guacd_proc_map* map) { 259 260 /* Free the list of all processes */ 261 guac_common_list_free(map->processes, NULL); 262 263 /* Free each bucket */ 264 guac_common_list** buckets = map->__buckets; 265 int i; 266 for (i = 0; i < GUACD_PROC_MAP_BUCKETS; i++) { 267 guac_common_list_free(*(buckets + i), free); 268 } 269 270} 271