resource: add resource.method.recalc(zone_name) function
[profile/ivi/murphy.git] / src / resource / resource-owner.c
1 /*
2  * Copyright (c) 2012, Intel Corporation
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *  * Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  *  * Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *  * Neither the name of Intel Corporation nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <stdio.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <errno.h>
34
35 #include <murphy/common/mm.h>
36 #include <murphy/common/hashtbl.h>
37 #include <murphy/common/utils.h>
38 #include <murphy/common/log.h>
39
40 #include <murphy-db/mqi.h>
41
42 #include <murphy/resource/client-api.h>
43 #include <murphy/resource/config-api.h>
44
45 #include "resource-owner.h"
46 #include "application-class.h"
47 #include "resource-set.h"
48 #include "resource.h"
49 #include "zone.h"
50 #include "resource-lua.h"
51
52 #define NAME_LENGTH          24
53
54 #define ZONE_ID_IDX          0
55 #define ZONE_NAME_IDX        1
56 #define CLASS_NAME_IDX       2
57 #define FIRST_ATTRIBUTE_IDX  3
58
59 typedef struct {
60     uint32_t          zone_id;
61     const char       *zone_name;
62     const char       *class_name;
63     mrp_attr_value_t  attrs[MQI_COLUMN_MAX];
64 } owner_row_t;
65
66 static mrp_resource_owner_t  resource_owners[MRP_ZONE_MAX * MRP_RESOURCE_MAX];
67 static mqi_handle_t          owner_tables[MRP_RESOURCE_MAX];
68
69 static mrp_resource_owner_t *get_owner(uint32_t, uint32_t);
70 static void reset_owners(uint32_t, mrp_resource_owner_t *);
71 static bool grant_ownership(mrp_resource_owner_t *, mrp_zone_t *,
72                             mrp_application_class_t *, mrp_resource_set_t *,
73                             mrp_resource_t *);
74 static bool advice_ownership(mrp_resource_owner_t *, mrp_zone_t *,
75                              mrp_application_class_t *, mrp_resource_set_t *,
76                              mrp_resource_t *);
77
78 static void manager_start_transaction(mrp_zone_t *);
79 static void manager_end_transaction(mrp_zone_t *);
80
81 static void delete_resource_owner(mrp_zone_t *, mrp_resource_t *);
82 static void insert_resource_owner(mrp_zone_t *, mrp_application_class_t *,
83                                   mrp_resource_t *);
84 static void update_resource_owner(mrp_zone_t *, mrp_application_class_t *,
85                                   mrp_resource_t *);
86 static void set_attr_descriptors(mqi_column_desc_t *, mrp_resource_t *);
87
88
89 int mrp_resource_owner_create_database_table(mrp_resource_def_t *rdef)
90 {
91     MQI_COLUMN_DEFINITION_LIST(base_coldefs,
92         MQI_COLUMN_DEFINITION( "zone_id"          , MQI_UNSIGNED             ),
93         MQI_COLUMN_DEFINITION( "zone_name"        , MQI_VARCHAR(NAME_LENGTH) ),
94         MQI_COLUMN_DEFINITION( "application_class", MQI_VARCHAR(NAME_LENGTH) )
95     );
96
97     MQI_INDEX_DEFINITION(indexdef,
98         MQI_INDEX_COLUMN( "zone_id" )
99     );
100
101     static bool initialized = false;
102
103     char name[256];
104     mqi_column_def_t  coldefs[MQI_COLUMN_MAX + 1];
105     mqi_column_def_t *col;
106     mrp_attr_def_t *atd;
107     mqi_handle_t table;
108     char c, *p;
109     size_t i,j;
110
111     if (!initialized) {
112         mqi_open();
113         for (i = 0;  i < MRP_RESOURCE_MAX;  i++)
114             owner_tables[i] = MQI_HANDLE_INVALID;
115         initialized = true;
116     }
117
118     MRP_ASSERT(sizeof(base_coldefs) < sizeof(coldefs),"too many base columns");
119     MRP_ASSERT(rdef, "invalid argument");
120     MRP_ASSERT(rdef->id < MRP_RESOURCE_MAX, "confused with data structures");
121     MRP_ASSERT(owner_tables[rdef->id] == MQI_HANDLE_INVALID,
122                "owner table already exist");
123
124     snprintf(name, sizeof(name), "%s_owner", rdef->name);
125     for (p = name; (c = *p);  p++) {
126         if (!isascii(c) || (!isalnum(c) && c != '_'))
127             *p = '_';
128     }
129
130     j = MQI_DIMENSION(base_coldefs) - 1;
131     memcpy(coldefs, base_coldefs, j * sizeof(mqi_column_def_t));
132
133     for (i = 0;  i < rdef->nattr && j < MQI_COLUMN_MAX;  i++, j++) {
134         col = coldefs + j;
135         atd = rdef->attrdefs + i;
136
137         col->name   = atd->name;
138         col->type   = atd->type;
139         col->length = (col->type == mqi_string) ? NAME_LENGTH : 0;
140         col->flags  = 0;
141     }
142
143     memset(coldefs + j, 0, sizeof(mqi_column_def_t));
144
145     table = MQI_CREATE_TABLE(name, MQI_TEMPORARY, coldefs, indexdef);
146
147     if (table == MQI_HANDLE_INVALID) {
148         mrp_log_error("Can't create table '%s': %s", name, strerror(errno));
149         return -1;
150     }
151
152     owner_tables[rdef->id] = table;
153
154     return 0;
155 }
156
157
158 void mrp_resource_owner_update_zone(uint32_t zoneid,
159                                     mrp_resource_set_t *reqset,
160                                     uint32_t reqid)
161 {
162     typedef struct {
163         uint32_t replyid;
164         mrp_resource_set_t *rset;
165         bool move;
166     } event_t;
167
168     mrp_resource_owner_t oldowners[MRP_RESOURCE_MAX];
169     mrp_resource_owner_t backup[MRP_RESOURCE_MAX];
170     mrp_zone_t *zone;
171     mrp_application_class_t *class;
172     mrp_resource_set_t *rset;
173     mrp_resource_t *res;
174     mrp_resource_def_t *rdef;
175     mrp_resource_mgr_ftbl_t *ftbl;
176     mrp_resource_owner_t *owner, *old, *owners;
177     mrp_resource_mask_t mask;
178     mrp_resource_mask_t mandatory;
179     mrp_resource_mask_t grant;
180     mrp_resource_mask_t advice;
181     void *clc, *rsc, *rc;
182     uint32_t rid;
183     uint32_t rcnt;
184     bool force_release;
185     bool changed;
186     bool move;
187     mrp_resource_event_t notify;
188     uint32_t replyid;
189     uint32_t nevent, maxev;
190     event_t *events, *ev, *lastev;
191
192     MRP_ASSERT(zoneid < MRP_ZONE_MAX, "invalid argument");
193
194     zone = mrp_zone_find_by_id(zoneid);
195
196     MRP_ASSERT(zone, "zone is not defined");
197
198     if (!(maxev = mrp_get_resource_set_count()))
199         return;
200
201     nevent = 0;
202     events = mrp_alloc(sizeof(event_t) * maxev);
203
204     MRP_ASSERT(events, "Memory alloc failure. Can't update zone");
205
206     reset_owners(zoneid, oldowners);
207     manager_start_transaction(zone);
208
209     rcnt = mrp_resource_definition_count();
210     clc  = NULL;
211
212     while ((class = mrp_application_class_iterate_classes(&clc))) {
213         rsc = NULL;
214
215         while ((rset=mrp_application_class_iterate_rsets(class,zoneid,&rsc))) {
216             force_release = false;
217             mandatory = rset->resource.mask.mandatory;
218             grant = 0;
219             advice = 0;
220             rc = NULL;
221
222             switch (rset->state) {
223
224             case mrp_resource_acquire:
225                 while ((res = mrp_resource_set_iterate_resources(rset, &rc))) {
226                     rdef  = res->def;
227                     rid   = rdef->id;
228                     owner = get_owner(zoneid, rid);
229
230                     backup[rid] = *owner;
231
232                     if (grant_ownership(owner, zone, class, rset, res))
233                         grant |= ((mrp_resource_mask_t)1 << rid);
234                     else {
235                         if (owner->rset != rset)
236                             force_release |= owner->modal;
237                     }
238                 }
239                 owners = get_owner(zoneid, 0);
240                 if ((grant & mandatory) == mandatory &&
241                     mrp_resource_lua_veto(zone, rset, owners, grant))
242                 {
243                     advice = grant;
244                 }
245                 else {
246                     /* rollback, ie. restore the backed up state */
247                     rc = NULL;
248                     while ((res=mrp_resource_set_iterate_resources(rset,&rc))){
249                          rdef  = res->def;
250                          rid   = rdef->id;
251                          mask  = (mrp_resource_mask_t)1 << rid;
252                          owner = get_owner(zoneid, rid);
253                         *owner = backup[rid];
254
255                         if ((grant & mask)) {
256                             if ((ftbl = rdef->manager.ftbl) && ftbl->free)
257                                 ftbl->free(zone, res, rdef->manager.userdata);
258                         }
259
260                         if (advice_ownership(owner, zone, class, rset, res))
261                             advice |= mask;
262                     }
263
264                     grant = 0;
265
266                     if ((advice & mandatory) != mandatory)
267                         advice = 0;
268
269                     mrp_resource_lua_set_owners(zone, owners);
270                 }
271                 break;
272
273             case mrp_resource_release:
274                 while ((res = mrp_resource_set_iterate_resources(rset, &rc))) {
275                     rdef  = res->def;
276                     rid   = rdef->id;
277                     owner = get_owner(zoneid, rid);
278
279                     if (advice_ownership(owner, zone, class, rset, res))
280                         advice |= ((mrp_resource_mask_t)1 << rid);
281                 }
282                 if ((advice & mandatory) != mandatory)
283                     advice = 0;
284                 break;
285
286             default:
287                 break;
288             }
289
290             changed = false;
291             move    = false;
292             notify  = 0;
293             replyid = (reqset == rset && reqid == rset->request.id) ? reqid:0;
294
295
296             if (force_release) {
297                 move = (rset->state != mrp_resource_release);
298                 notify = move ? MRP_RESOURCE_EVENT_RELEASE : 0;
299                 changed = move || rset->resource.mask.grant;
300                 rset->state = mrp_resource_release;
301                 rset->resource.mask.grant = 0;
302             }
303             else {
304                 if (grant == rset->resource.mask.grant) {
305                     if (rset->state == mrp_resource_acquire &&
306                         !grant && rset->dont_wait)
307                     {
308                         notify = MRP_RESOURCE_EVENT_RELEASE;
309                         rset->state = mrp_resource_release;
310                         move = true;
311                     }
312                 }
313                 else {
314                     rset->resource.mask.grant = grant;
315                     changed = true;
316
317                     if (!grant && rset->auto_release) {
318                         if (rset->state != mrp_resource_release)
319                             notify = MRP_RESOURCE_EVENT_RELEASE;
320                         rset->state = mrp_resource_release;
321                         move = true;
322                     }
323                 }
324             }
325
326             if (notify) {
327                 mrp_resource_set_notify(rset, notify);
328             }
329
330             if (advice != rset->resource.mask.advice) {
331                 rset->resource.mask.advice = advice;
332                 changed = true;
333             }
334
335             if (replyid || changed) {
336                 ev = events + nevent++;
337
338                 ev->replyid = replyid;
339                 ev->rset    = rset;
340                 ev->move    = move;
341             }
342         } /* while rset */
343     } /* while class */
344
345     manager_end_transaction(zone);
346
347     for (lastev = (ev = events) + nevent;     ev < lastev;     ev++) {
348         rset = ev->rset;
349
350         if (ev->move)
351             mrp_application_class_move_resource_set(rset);
352
353         mrp_resource_set_updated(rset);
354
355         if (rset->event)
356             rset->event(ev->replyid, rset, rset->user_data);
357     }
358
359     mrp_free(events);
360
361     for (rid = 0;  rid < rcnt;  rid++) {
362         owner = get_owner(zoneid, rid);
363         old   = oldowners + rid;
364
365         if (owner->class != old->class ||
366             owner->rset  != old->rset  ||
367             owner->res   != old->res     )
368         {
369             if (!owner->res)
370                 delete_resource_owner(zone, old->res);
371             else if (!old->res)
372                 insert_resource_owner(zone, owner->class, owner->res);
373             else
374                 update_resource_owner(zone, owner->class, owner->res);
375         }
376     }
377 }
378
379 int mrp_resource_owner_print(char *buf, int len)
380 {
381 #define PRINT(fmt, args...)  if (p<e) { p += snprintf(p, e-p, fmt , ##args); }
382
383     mrp_zone_t *zone;
384     mrp_resource_owner_t *owner;
385     mrp_application_class_t *class;
386     mrp_resource_set_t *rset;
387     mrp_resource_t *res;
388     mrp_resource_def_t *rdef;
389     uint32_t rcnt, rid;
390     uint32_t zcnt, zid;
391     char *p, *e;
392
393     MRP_ASSERT(buf && len > 0, "invalid argument");
394
395     rcnt = mrp_resource_definition_count();
396     zcnt = mrp_zone_count();
397
398     e = (p = buf) + len;
399
400     PRINT("Resource owners:\n");
401
402     for (zid = 0;  zid < zcnt;  zid++) {
403         zone = mrp_zone_find_by_id(zid);
404
405         if (!zone) {
406             PRINT("   Zone %u:\n", zid);
407         }
408         else {
409             PRINT("   Zone %s:", zone->name);
410             p += mrp_zone_attribute_print(zone, p, e-p);
411             PRINT("\n");
412         }
413
414         for (rid = 0;   rid < rcnt;   rid++) {
415             if (!(rdef = mrp_resource_definition_find_by_id(rid)))
416                 continue;
417
418             PRINT("      %-15s: ", rdef->name);
419
420             owner = get_owner(zid, rid);
421
422             if (!(class = owner->class) ||
423                 !(rset  = owner->rset ) ||
424                 !(res   = owner->res  )    )
425             {
426                 PRINT("<nobody>");
427             }
428             else {
429                 MRP_ASSERT(rdef == res->def, "confused with data structures");
430
431                 PRINT("%-15s", class->name);
432
433                 p += mrp_resource_attribute_print(res, p, e-p);
434             }
435
436             PRINT("\n");
437         }
438     }
439
440     return p - buf;
441
442 #undef PRINT
443 }
444
445
446 static mrp_resource_owner_t *get_owner(uint32_t zone, uint32_t resid)
447 {
448     MRP_ASSERT(zone < MRP_ZONE_MAX && resid < MRP_RESOURCE_MAX,
449                "invalid argument");
450
451     return resource_owners + (zone * MRP_RESOURCE_MAX + resid);
452 }
453
454 static void reset_owners(uint32_t zone, mrp_resource_owner_t *oldowners)
455 {
456     mrp_resource_owner_t *owners = get_owner(zone, 0);
457     size_t size = sizeof(mrp_resource_owner_t) * MRP_RESOURCE_MAX;
458     size_t i;
459
460     if (oldowners)
461         memcpy(oldowners, owners, size);
462
463     memset(owners, 0, size);
464
465     for (i = 0;   i < MRP_RESOURCE_MAX;   i++)
466         owners[i].share = true;
467 }
468
469 static bool grant_ownership(mrp_resource_owner_t    *owner,
470                             mrp_zone_t              *zone,
471                             mrp_application_class_t *class,
472                             mrp_resource_set_t      *rset,
473                             mrp_resource_t          *res)
474 {
475     mrp_resource_def_t      *rdef = res->def;
476     mrp_resource_mgr_ftbl_t *ftbl = rdef->manager.ftbl;
477     bool                     set_owner = false;
478
479     /*
480       if (forbid_grant())
481         return false;
482      */
483
484     if (owner->modal)
485         return false;
486
487     do { /* not a loop */
488         if (!owner->class && !owner->rset) {
489             /* nobody owns this, so grab it */
490             set_owner = true;
491             break;
492         }
493
494         if (owner->class == class && owner->rset == rset) {
495             /* we happen to already own it */
496             break;
497         }
498
499         if (rdef->shareable && owner->share) {
500             /* OK, someone else owns it but
501                the owner is ready to share it with us */
502             break;
503         }
504
505         return false;
506
507     } while(0);
508
509     if (ftbl && ftbl->allocate) {
510         if (!ftbl->allocate(zone, res, rdef->manager.userdata))
511             return false;
512     }
513
514     if (set_owner) {
515         owner->class = class;
516         owner->rset  = rset;
517         owner->res   = res;
518         owner->modal = class->modal;
519     }
520
521     owner->share = class->share && res->shared;
522
523     return true;
524 }
525
526 static bool advice_ownership(mrp_resource_owner_t    *owner,
527                              mrp_zone_t              *zone,
528                              mrp_application_class_t *class,
529                              mrp_resource_set_t      *rset,
530                              mrp_resource_t          *res)
531 {
532     mrp_resource_def_t      *rdef = res->def;
533     mrp_resource_mgr_ftbl_t *ftbl = rdef->manager.ftbl;
534
535     (void)zone;
536
537     /*
538       if (forbid_grant())
539         return false;
540      */
541
542     if (owner->modal)
543         return false;
544
545     do { /* not a loop */
546         if (!owner->class && !owner->rset)
547             /* nobody owns this */
548             break;
549
550         if (owner->share)
551             /* someone else owns it but it can be shared */
552             break;
553
554
555         if (owner->class == class) {
556             if (owner->rset->class.priority == rset->class.priority)
557                 break;
558         }
559
560         return false;
561
562     } while(0);
563
564     if (ftbl && ftbl->advice) {
565         if (!ftbl->advice(zone, res, rdef->manager.userdata))
566             return false;
567     }
568
569     return true;
570 }
571
572 static void manager_start_transaction(mrp_zone_t *zone)
573 {
574     mrp_resource_def_t *rdef;
575     mrp_resource_mgr_ftbl_t *ftbl;
576     void *cursor = NULL;
577
578     while ((rdef = mrp_resource_definition_iterate_manager(&cursor))) {
579         ftbl = rdef->manager.ftbl;
580
581         MRP_ASSERT(ftbl, "confused with data structures");
582
583         if (ftbl->init)
584             ftbl->init(zone, rdef->manager.userdata);
585     }
586 }
587
588 static void manager_end_transaction(mrp_zone_t *zone)
589 {
590     mrp_resource_def_t *rdef;
591     mrp_resource_mgr_ftbl_t *ftbl;
592     void *cursor = NULL;
593
594     while ((rdef = mrp_resource_definition_iterate_manager(&cursor))) {
595         ftbl = rdef->manager.ftbl;
596
597         MRP_ASSERT(ftbl, "confused with data structures");
598
599         if (ftbl->commit)
600             ftbl->commit(zone, rdef->manager.userdata);
601     }
602 }
603
604
605 static void delete_resource_owner(mrp_zone_t *zone, mrp_resource_t *res)
606 {
607     static uint32_t zone_id;
608
609     MQI_WHERE_CLAUSE(where,
610         MQI_EQUAL( MQI_COLUMN(0), MQI_UNSIGNED_VAR(zone_id) )
611     );
612
613     mrp_resource_def_t *rdef;
614     int n;
615
616     MRP_ASSERT(res, "invalid argument");
617
618     rdef = res->def;
619     zone_id = zone->id;
620
621     if ((n = MQI_DELETE(owner_tables[rdef->id], where)) != 1)
622         mrp_log_error("Could not delete resource owner");
623 }
624
625 static void insert_resource_owner(mrp_zone_t *zone,
626                                   mrp_application_class_t *class,
627                                   mrp_resource_t *res)
628 {
629     mrp_resource_def_t *rdef = res->def;
630     uint32_t i;
631     int n;
632     owner_row_t row;
633     owner_row_t *rows[2];
634     mqi_column_desc_t cdsc[FIRST_ATTRIBUTE_IDX + MQI_COLUMN_MAX + 1];
635
636     MRP_ASSERT(FIRST_ATTRIBUTE_IDX + rdef->nattr <= MQI_COLUMN_MAX,
637                "too many attributes for a table");
638
639     row.zone_id    = zone->id;
640     row.zone_name  = zone->name;
641     row.class_name = class->name;
642     memcpy(row.attrs, res->attrs, rdef->nattr * sizeof(mrp_attr_value_t));
643
644     i = 0;
645     cdsc[i].cindex = ZONE_ID_IDX;
646     cdsc[i].offset = MQI_OFFSET(owner_row_t, zone_id);
647
648     i++;
649     cdsc[i].cindex = ZONE_NAME_IDX;
650     cdsc[i].offset = MQI_OFFSET(owner_row_t, zone_name);
651
652     i++;
653     cdsc[i].cindex = CLASS_NAME_IDX;
654     cdsc[i].offset = MQI_OFFSET(owner_row_t, class_name);
655
656     set_attr_descriptors(cdsc + (i+1), res);
657
658     rows[0] = &row;
659     rows[1] = NULL;
660
661     if ((n = MQI_INSERT_INTO(owner_tables[rdef->id], cdsc, rows)) != 1)
662         mrp_log_error("can't insert row into owner table");
663 }
664
665 static void update_resource_owner(mrp_zone_t *zone,
666                                   mrp_application_class_t *class,
667                                   mrp_resource_t *res)
668 {
669     static uint32_t zone_id;
670
671     MQI_WHERE_CLAUSE(where,
672         MQI_EQUAL( MQI_COLUMN(0), MQI_UNSIGNED_VAR(zone_id) )
673     );
674
675     mrp_resource_def_t *rdef = res->def;
676     uint32_t i;
677     int n;
678     owner_row_t row;
679     mqi_column_desc_t cdsc[FIRST_ATTRIBUTE_IDX + MQI_COLUMN_MAX + 1];
680
681     zone_id = zone->id;
682
683     MRP_ASSERT(1 + rdef->nattr <= MQI_COLUMN_MAX,
684                "too many attributes for a table");
685
686     row.class_name = class->name;
687     memcpy(row.attrs, res->attrs, rdef->nattr * sizeof(mrp_attr_value_t));
688
689     i = 0;
690     cdsc[i].cindex = CLASS_NAME_IDX;
691     cdsc[i].offset = MQI_OFFSET(owner_row_t, class_name);
692
693     set_attr_descriptors(cdsc + (i+1), res);
694
695
696     if ((n = MQI_UPDATE(owner_tables[rdef->id], cdsc, &row, where)) != 1)
697         mrp_log_error("can't update row in owner table");
698 }
699
700
701 static void set_attr_descriptors(mqi_column_desc_t *cdsc, mrp_resource_t *res)
702 {
703     mrp_resource_def_t *rdef = res->def;
704     uint32_t i,j;
705     int o;
706
707     for (i = j = 0;  j < rdef->nattr;  j++) {
708         switch (rdef->attrdefs[j].type) {
709         case mqi_string:   o = MQI_OFFSET(owner_row_t,attrs[j].string);  break;
710         case mqi_integer:  o = MQI_OFFSET(owner_row_t,attrs[j].integer); break;
711         case mqi_unsignd:  o = MQI_OFFSET(owner_row_t,attrs[j].unsignd); break;
712         case mqi_floating: o = MQI_OFFSET(owner_row_t,attrs[j].floating);break;
713         default:           /* skip this */                            continue;
714         }
715
716         cdsc[i].cindex = FIRST_ATTRIBUTE_IDX + j;
717         cdsc[i].offset = o;
718         i++;
719     }
720
721     cdsc[i].cindex = -1;
722     cdsc[i].offset =  1;
723 }
724
725
726 /*
727  * Local Variables:
728  * c-basic-offset: 4
729  * indent-tabs-mode: nil
730  * End:
731  *
732  */