File permission rework
[profile/ivi/persistence-client-library.git] / src / persistence_client_library_key.c
1 /******************************************************************************
2  * Project         Persistency
3  * (c) copyright   2012
4  * Company         XS Embedded GmbH
5  *****************************************************************************/
6 /******************************************************************************
7  * This Source Code Form is subject to the terms of the
8  * Mozilla Public License, v. 2.0. If a  copy of the MPL was not distributed
9  * with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 ******************************************************************************/
11  /**
12  * @file           persistence_client_library_key.c
13  * @ingroup        Persistence client library
14  * @author         Ingo Huerner
15  * @brief          Implementation of the persistence client library.
16  *                 Library provides an API to access persistent data
17  * @see            
18  */
19
20 #include "persistence_client_library_key.h"
21
22 #include "../include_protected/persistence_client_library_db_access.h"
23 #include "../include_protected/persistence_client_library_rc_table.h"
24 #include "../include_protected/crc32.h"
25
26 #include "persistence_client_library_handle.h"
27 #include "persistence_client_library_pas_interface.h"
28 #include "persistence_client_library_prct_access.h"
29 #include "persistence_client_library_custom_loader.h"
30
31
32 // ----------------------------------------------------------------------------
33 // ----------------------------------------------------------------------------
34 // function with handle
35 // ----------------------------------------------------------------------------
36 // ----------------------------------------------------------------------------
37
38 int pclKeyHandleOpen(unsigned int ldbid, const char* resource_id, unsigned int user_no, unsigned int seat_no)
39 {
40    int handle = EPERS_NOT_INITIALIZED;
41
42    if(gPclInitialized >= PCLinitialized)
43    {
44       PersistenceInfo_s dbContext;
45
46       char dbKey[DbKeyMaxLen]   = {0};      // database key
47       char dbPath[DbPathMaxLen] = {0};    // database location
48
49       //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclKeyHandleOpen: "), DLT_INT(ldbid), DLT_STRING(resource_id) );
50       dbContext.context.ldbid   = ldbid;
51       dbContext.context.seat_no = seat_no;
52       dbContext.context.user_no = user_no;
53
54       // get database context: database path and database key
55       handle = get_db_context(&dbContext, resource_id, ResIsNoFile, dbKey, dbPath);
56       if(   (handle >= 0)
57          && (dbContext.configKey.type == PersistenceResourceType_key) )          // check if type matches
58       {
59          if(dbContext.configKey.storage < PersistenceStorage_LastEntry)    // check if store policy is valid
60          {
61             if(PersistenceStorage_custom ==  dbContext.configKey.storage)
62             {
63                int idx =  custom_client_name_to_id(dbPath, 1);
64                char workaroundPath[128];  // workaround, because /sys/ can not be accessed on host!!!!
65                snprintf(workaroundPath, 128, "%s%s", "/Data", dbPath  );
66
67                if( (idx < PersCustomLib_LastEntry) && (gPersCustomFuncs[idx].custom_plugin_handle_open != NULL) )
68                {
69                   int flag = 0, mode = 0;
70                   handle = gPersCustomFuncs[idx].custom_plugin_handle_open(workaroundPath, flag, mode);
71                }
72                else
73                {
74                   handle = EPERS_NOPLUGINFUNCT;
75                }
76             }
77             else
78             {
79                handle = get_persistence_handle_idx();
80             }
81
82             if((handle < MaxPersHandle) && (0 <= handle))
83             {
84                // remember data in handle array
85                strncpy(gKeyHandleArray[handle].dbPath, dbPath, DbPathMaxLen);
86                strncpy(gKeyHandleArray[handle].dbKey,  dbKey,  DbKeyMaxLen);
87                strncpy(gKeyHandleArray[handle].resourceID,  resource_id,  DbResIDMaxLen);
88                gKeyHandleArray[handle].dbPath[DbPathMaxLen-1] = '\0'; // Ensures 0-Termination
89                gKeyHandleArray[handle].dbKey[ DbPathMaxLen-1] = '\0'; // Ensures 0-Termination
90                gKeyHandleArray[handle].info = dbContext;
91             }
92             else
93             {
94                DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclKeyHandleOpen: error - handleId out of bounds:"), DLT_INT(handle));
95             }
96          }
97       }
98       else
99       {
100          DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclKeyHandleOpen: error - no database context or resource is not a key "));
101       }
102    }
103
104
105    return handle;
106 }
107
108
109
110 int pclKeyHandleClose(int key_handle)
111 {
112    int rval = EPERS_NOT_INITIALIZED;
113
114    //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclKeyHandleClose: "),
115    //                DLT_INT(gKeyHandleArray[key_handle].info.context.ldbid), DLT_STRING(gKeyHandleArray[key_handle].resourceID) );
116
117    if(gPclInitialized >= PCLinitialized)
118    {
119       if(key_handle < MaxPersHandle)
120       {
121          if(PersistenceStorage_custom == gKeyHandleArray[key_handle].info.configKey.storage )
122          {
123             int idx =  custom_client_name_to_id(gKeyHandleArray[key_handle].dbPath, 1);
124
125             if( (idx < PersCustomLib_LastEntry) && (gPersCustomFuncs[idx].custom_plugin_handle_close != NULL) )
126             {
127                rval = gPersCustomFuncs[idx].custom_plugin_handle_close(key_handle);
128             }
129             else
130             {
131                rval = EPERS_NOPLUGINFUNCT;
132             }
133          }
134          else
135          {
136             set_persistence_handle_close_idx(key_handle);
137          }
138
139          // invalidate entries
140          memset(gKeyHandleArray[key_handle].dbPath, 0, DbPathMaxLen);
141          memset(gKeyHandleArray[key_handle].dbKey  ,0, DbKeyMaxLen);
142          gKeyHandleArray[key_handle].info.configKey.storage = -1;
143       }
144       else
145       {
146          rval = EPERS_MAXHANDLE;
147       }
148    }
149
150    return rval;
151 }
152
153
154
155 int pclKeyHandleGetSize(int key_handle)
156 {
157    int size = EPERS_NOT_INITIALIZED;
158
159    //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclKeyHandleGetSize: "),
160    //                DLT_INT(gKeyHandleArray[key_handle].info.context.ldbid), DLT_STRING(gKeyHandleArray[key_handle].resourceID) );
161
162    if(gPclInitialized >= PCLinitialized)
163    {
164       if(key_handle < MaxPersHandle)
165       {
166          if(PersistenceStorage_custom ==  gKeyHandleArray[key_handle].info.configKey.storage)
167          {
168             int idx =  custom_client_name_to_id(gKeyHandleArray[key_handle].dbPath, 1);
169
170             if( (idx < PersCustomLib_LastEntry) && (gPersCustomFuncs[idx].custom_plugin_get_size != NULL) )
171             {
172                size = gPersCustomFuncs[idx].custom_plugin_get_size(gKeyHandleArray[key_handle].dbPath);
173             }
174             else
175             {
176                size = EPERS_NOPLUGINFUNCT;
177             }
178          }
179          else
180          {
181             size = pers_db_get_key_size(gKeyHandleArray[key_handle].dbPath, gKeyHandleArray[key_handle].dbKey,
182                                              &gKeyHandleArray[key_handle].info);
183          }
184       }
185       else
186       {
187          size = EPERS_MAXHANDLE;
188       }
189    }
190
191    return size;
192 }
193
194
195
196 int pclKeyHandleReadData(int key_handle, unsigned char* buffer, int buffer_size)
197 {
198    int size = EPERS_NOT_INITIALIZED;
199
200    //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclKeyHandleReadData: "),
201    //             DLT_INT(gKeyHandleArray[key_handle].info.context.ldbid), DLT_STRING(gKeyHandleArray[key_handle].resourceID) );
202
203    if(gPclInitialized >= PCLinitialized)
204    {
205       if(key_handle < MaxPersHandle)
206       {
207          if(PersistenceStorage_custom ==  gKeyHandleArray[key_handle].info.configKey.storage)
208          {
209             int idx =  custom_client_name_to_id(gKeyHandleArray[key_handle].dbPath, 1);
210
211             if( (idx < PersCustomLib_LastEntry) && (gPersCustomFuncs[idx].custom_plugin_handle_get_data != NULL) )
212             {
213                size = gPersCustomFuncs[idx].custom_plugin_handle_get_data(key_handle, (char*)buffer, buffer_size-1);
214             }
215             else
216             {
217                size = EPERS_NOPLUGINFUNCT;
218             }
219          }
220          else
221          {
222             size = pers_db_read_key(gKeyHandleArray[key_handle].dbPath, gKeyHandleArray[key_handle].dbKey,
223                                         &gKeyHandleArray[key_handle].info, buffer, buffer_size);
224          }
225       }
226       else
227       {
228          size = EPERS_MAXHANDLE;
229       }
230    }
231
232    return size;
233 }
234
235
236
237 int pclKeyHandleRegisterNotifyOnChange(int key_handle, pclChangeNotifyCallback_t callback)
238 {
239    //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclKeyHandleRegisterNotifyOnChange: "),
240    //            DLT_INT(gKeyHandleArray[key_handle].info.context.ldbid), DLT_STRING(gKeyHandleArray[key_handle].resourceID) );
241
242    return handleRegNotifyOnChange(key_handle, callback, Notify_register);
243 }
244
245 int pclKeyHandleUnRegisterNotifyOnChange(int key_handle, pclChangeNotifyCallback_t callback)
246 {
247    //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclKeyHandleUnRegisterNotifyOnChange: "),
248    //         DLT_INT(gKeyHandleArray[key_handle].info.context.ldbid), DLT_STRING(gKeyHandleArray[key_handle].resourceID) );
249
250    return handleRegNotifyOnChange(key_handle, callback, Notify_unregister);
251 }
252
253
254
255 int handleRegNotifyOnChange(int key_handle, pclChangeNotifyCallback_t callback, PersNotifyRegPolicy_e regPolicy)
256 {
257    int rval = EPERS_NOT_INITIALIZED;
258
259    if(gPclInitialized >= PCLinitialized)
260    {
261       if(key_handle < MaxPersHandle)
262       {
263          rval = regNotifyOnChange(gKeyHandleArray[key_handle].info.context.ldbid,
264                                   gKeyHandleArray[key_handle].resourceID,
265                                   gKeyHandleArray[key_handle].info.context.user_no,
266                                   gKeyHandleArray[key_handle].info.context.seat_no,
267                                   callback,
268                                   regPolicy);
269       }
270       else
271       {
272          rval = EPERS_MAXHANDLE;
273       }
274    }
275    return rval;
276 }
277
278
279
280 int pclKeyHandleWriteData(int key_handle, unsigned char* buffer, int buffer_size)
281 {
282    int size = EPERS_NOT_INITIALIZED;
283
284    //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclKeyHandleWriteData: "),
285    //                DLT_INT(gKeyHandleArray[key_handle].info.context.ldbid), DLT_STRING(gKeyHandleArray[key_handle].resourceID) );
286
287    if(gPclInitialized >= PCLinitialized)
288    {
289       if(AccessNoLock != isAccessLocked() )     // check if access to persistent data is locked
290       {
291          if(buffer_size <= gMaxKeyValDataSize)  // check data size
292          {
293             if(key_handle < MaxPersHandle)
294             {
295                if(gKeyHandleArray[key_handle].info.configKey.permission != PersistencePermission_ReadOnly)  // don't write to a read only resource
296                {
297                   if(PersistenceStorage_custom ==  gKeyHandleArray[key_handle].info.configKey.storage)
298                   {
299                      int idx =  custom_client_name_to_id(gKeyHandleArray[key_handle].dbPath, 1);
300
301                      if( (idx < PersCustomLib_LastEntry) && (gPersCustomFuncs[idx].custom_plugin_handle_set_data != NULL) )
302                      {
303                         size = gPersCustomFuncs[idx].custom_plugin_handle_set_data(key_handle, (char*)buffer, buffer_size-1);
304
305                         if(size >= 0) // success ==> send change notification
306                         {
307                            int rval = pers_send_Notification_Signal(gKeyHandleArray[key_handle].dbKey,
308                                                                   &(gKeyHandleArray[key_handle].info.context), pclNotifyStatus_changed);
309
310                            if(rval <= 0)
311                            {
312                               DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclKeyHandleWriteData: error - failed to send notification"));
313                               size = rval;
314                            }
315                         }
316                      }
317                      else
318                      {
319                         size = EPERS_NOPLUGINFUNCT;
320                      }
321                   }
322                   else
323                   {
324                      size = pers_db_write_key(gKeyHandleArray[key_handle].dbPath, gKeyHandleArray[key_handle].dbKey,
325                                               &gKeyHandleArray[key_handle].info, buffer, buffer_size);
326                   }
327                }
328                else
329                {
330                   size = EPERS_RESOURCE_READ_ONLY;
331                }
332
333             }
334             else
335             {
336                size = EPERS_MAXHANDLE;
337             }
338          }
339          else
340          {
341             DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclKeyHandleWriteData: error - buffer_size to big, limit is [bytes]:"), DLT_INT(gMaxKeyValDataSize));
342             size = EPERS_MAX_BUFF_SIZE;
343          }
344       }
345       else
346       {
347          size = EPERS_LOCKFS;
348       }
349    }
350
351    return size;
352 }
353
354
355
356
357
358 // ----------------------------------------------------------------------------
359 // ----------------------------------------------------------------------------
360 // functions to be used directly without a handle
361 // ----------------------------------------------------------------------------
362 // ----------------------------------------------------------------------------
363
364 int pclKeyDelete(unsigned int ldbid, const char* resource_id, unsigned int user_no, unsigned int seat_no)
365 {
366    int rval = EPERS_NOT_INITIALIZED;
367
368    //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclKeyDelete: "), DLT_INT(ldbid), DLT_STRING(resource_id) );
369
370    if(gPclInitialized >= PCLinitialized)
371    {
372       if(AccessNoLock != isAccessLocked() ) // check if access to persistent data is locked
373       {
374          PersistenceInfo_s dbContext;
375
376         char dbKey[DbKeyMaxLen]   = {0};      // database key
377         char dbPath[DbPathMaxLen] = {0};    // database location
378
379         dbContext.context.ldbid   = ldbid;
380         dbContext.context.seat_no = seat_no;
381         dbContext.context.user_no = user_no;
382
383         // get database context: database path and database key
384         rval = get_db_context(&dbContext, resource_id, ResIsNoFile, dbKey, dbPath);
385         if(   (rval >= 0)
386            && (dbContext.configKey.type == PersistenceResourceType_key) )  // check if type is matching
387         {
388            if(   dbContext.configKey.storage < PersistenceStorage_LastEntry
389               && dbContext.configKey.storage >= PersistenceStorage_local)   // check if store policy is valid
390            {
391               rval = pers_db_delete_key(dbPath, dbKey, &dbContext);
392            }
393            else
394            {
395              rval = EPERS_BADPOL;
396            }
397         }
398       }
399       else
400       {
401          rval = EPERS_LOCKFS;
402       }
403    }
404
405    return rval;
406 }
407
408
409
410 // status: OK
411 int pclKeyGetSize(unsigned int ldbid, const char* resource_id, unsigned int user_no, unsigned int seat_no)
412 {
413    int data_size = EPERS_NOT_INITIALIZED;
414
415    if(gPclInitialized >= PCLinitialized)
416    {
417       PersistenceInfo_s dbContext;
418
419       char dbKey[DbKeyMaxLen]   = {0};      // database key
420       char dbPath[DbPathMaxLen] = {0};    // database location
421
422       dbContext.context.ldbid   = ldbid;
423       dbContext.context.seat_no = seat_no;
424       dbContext.context.user_no = user_no;
425
426       //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclKeyGetSize: "), DLT_INT(ldbid), DLT_STRING(resource_id) );
427
428       // get database context: database path and database key
429       data_size = get_db_context(&dbContext, resource_id, ResIsNoFile, dbKey, dbPath);
430       if(   (data_size >= 0)
431          && (dbContext.configKey.type == PersistenceResourceType_key) )    // check if type matches
432       {
433          if(   dbContext.configKey.storage < PersistenceStorage_LastEntry
434             && dbContext.configKey.storage >= PersistenceStorage_local)   // check if store policy is valid
435          {
436             data_size = pers_db_get_key_size(dbPath, dbKey, &dbContext);
437          }
438          else
439          {
440            data_size = EPERS_BADPOL;
441          }
442       }
443       else
444       {
445         data_size = EPERS_BADPOL;
446       }
447    }
448
449    return data_size;
450 }
451
452
453
454 // status: OK
455 int pclKeyReadData(unsigned int ldbid, const char* resource_id, unsigned int user_no, unsigned int seat_no,
456                   unsigned char* buffer, int buffer_size)
457 {
458    int data_size = EPERS_NOT_INITIALIZED;
459
460    //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclKeyReadData: "), DLT_INT(ldbid), DLT_STRING(resource_id) );
461
462    if(gPclInitialized >= PCLinitialized)
463    {
464       if(AccessNoLock != isAccessLocked() ) // check if access to persistent data is locked
465       {
466          PersistenceInfo_s dbContext;
467
468          char dbKey[DbKeyMaxLen]   = {0};      // database key
469          char dbPath[DbPathMaxLen] = {0};    // database location
470
471          dbContext.context.ldbid   = ldbid;
472          dbContext.context.seat_no = seat_no;
473          dbContext.context.user_no = user_no;
474
475          // get database context: database path and database key
476          data_size = get_db_context(&dbContext, resource_id, ResIsNoFile, dbKey, dbPath);
477          if(   (data_size >= 0)
478             && (dbContext.configKey.type == PersistenceResourceType_key) )
479          {
480
481             if(   dbContext.configKey.storage <  PersistenceStorage_LastEntry
482                && dbContext.configKey.storage >= PersistenceStorage_local)   // check if store policy is valid
483             {
484                   data_size = pers_db_read_key(dbPath, dbKey, &dbContext, buffer, buffer_size);
485             }
486             else
487             {
488                data_size = EPERS_BADPOL;
489             }
490          }
491          else
492          {
493             DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclKeyReadData - error - no database context or resource is not a key"));
494          }
495       }
496       else
497       {
498          data_size = EPERS_LOCKFS;
499       }
500    }
501
502    return data_size;
503 }
504
505
506
507 int pclKeyWriteData(unsigned int ldbid, const char* resource_id, unsigned int user_no, unsigned int seat_no,
508                    unsigned char* buffer, int buffer_size)
509 {
510    int data_size = EPERS_NOT_INITIALIZED;
511
512    //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclKeyWriteData: "), DLT_INT(ldbid), DLT_STRING(resource_id) );
513
514    if(gPclInitialized >= PCLinitialized)
515    {
516       if(AccessNoLock != isAccessLocked() )     // check if access to persistent data is locked
517       {
518          if(buffer_size <= gMaxKeyValDataSize)  // check data size
519          {
520             PersistenceInfo_s dbContext;
521
522             unsigned int hash_val_data = 0;
523
524             char dbKey[DbKeyMaxLen]   = {0};      // database key
525             char dbPath[DbPathMaxLen] = {0};    // database location
526
527             dbContext.context.ldbid   = ldbid;
528             dbContext.context.seat_no = seat_no;
529             dbContext.context.user_no = user_no;
530
531             // get database context: database path and database key
532             data_size = get_db_context(&dbContext, resource_id, ResIsNoFile, dbKey, dbPath);
533             if(   (data_size >= 0)
534                && (dbContext.configKey.type == PersistenceResourceType_key))
535             {
536                if(dbContext.configKey.permission != PersistencePermission_ReadOnly)  // don't write to a read only resource
537                {
538                   // get hash value of data to verify storing
539                   hash_val_data = pclCrc32(hash_val_data, buffer, buffer_size);
540
541                   // store data
542                   if(   dbContext.configKey.storage <  PersistenceStorage_LastEntry
543                      && dbContext.configKey.storage >= PersistenceStorage_local)   // check if store policy is valid
544                   {
545                      data_size = pers_db_write_key(dbPath, dbKey, &dbContext, buffer, buffer_size);
546                   }
547                   else
548                   {
549                      data_size = EPERS_BADPOL;
550                   }
551                }
552                else
553                {
554                   data_size = EPERS_RESOURCE_READ_ONLY;
555                }
556             }
557             else
558             {
559                DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclKeyWriteData - error - no database context or resource is not a key"));
560             }
561          }
562          else
563          {
564             data_size = EPERS_BUFLIMIT;
565             DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclKeyWriteData: error - buffer_size to big, limit is [bytes]:"), DLT_INT(gMaxKeyValDataSize));
566          }
567       }
568       else
569       {
570          data_size = EPERS_LOCKFS;
571       }
572    }
573
574    return data_size;
575 }
576
577
578
579 int pclKeyUnRegisterNotifyOnChange( unsigned int  ldbid, const char *  resource_id, unsigned int  user_no, unsigned int  seat_no, pclChangeNotifyCallback_t  callback)
580 {
581    //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclKeyUnRegisterNotifyOnChange: "),
582    //            DLT_INT(ldbid), DLT_STRING(resource_id) );
583
584    return regNotifyOnChange(ldbid, resource_id, user_no, seat_no, callback, Notify_unregister);
585 }
586
587
588 int pclKeyRegisterNotifyOnChange(unsigned int ldbid, const char* resource_id, unsigned int user_no, unsigned int seat_no, pclChangeNotifyCallback_t callback)
589 {
590    //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclKeyRegisterNotifyOnChange: "),
591    //            DLT_INT(ldbid), DLT_STRING(resource_id) );
592
593    return regNotifyOnChange(ldbid, resource_id, user_no, seat_no, callback, Notify_register);
594 }
595
596
597
598
599 int regNotifyOnChange(unsigned int ldbid, const char* resource_id, unsigned int user_no, unsigned int seat_no, pclChangeNotifyCallback_t callback, PersNotifyRegPolicy_e regPolicy)
600 {
601    int rval = EPERS_NOT_INITIALIZED;
602
603    if(gPclInitialized >= PCLinitialized)
604    {
605       PersistenceInfo_s dbContext;
606
607       //   unsigned int hash_val_data = 0;
608       char dbKey[DbKeyMaxLen]   = {0};      // database key
609       char dbPath[DbPathMaxLen] = {0};    // database location
610
611       //DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("pclKeyRegisterNotifyOnChange: "), DLT_INT(ldbid), DLT_STRING(resource_id) );
612
613       dbContext.context.ldbid   = ldbid;
614       dbContext.context.seat_no = seat_no;
615       dbContext.context.user_no = user_no;
616
617       // get database context: database path and database key
618       rval = get_db_context(&dbContext, resource_id, ResIsNoFile, dbKey, dbPath);
619
620       // registration is only on shared and custom keys possible
621       if(   (dbContext.configKey.storage != PersistenceStorage_local)
622         && (dbContext.configKey.type    == PersistenceResourceType_key) )
623       {
624          rval = persistence_notify_on_change(dbKey, ldbid, user_no, seat_no, callback, regPolicy);
625       }
626       else
627       {
628          DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pclKeyRegisterNotifyOnChange: error - resource is not a shared resource or resource is not a key"));
629          rval = EPERS_NOTIFY_NOT_ALLOWED;
630       }
631    }
632
633    return rval;
634 }
635
636
637
638
639
640
641