/* * %CopyrightBegin% * * Copyright Ericsson AB 1998-2016. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * %CopyrightEnd% * */ #include #include #include "eidef.h" #include "eiext.h" #include "reg.h" #include "eisend.h" #include "eirecv.h" #include "ei_connect_int.h" static int mn_start_restore(int fd, const erlang_pid *self, erlang_pid *mnesia, const char *mntab, long *count, long *maxkey,long *maxobj) { char buf[EISMALLBUF]; char *bufp=buf; char tmpbuf[64]; int index = 0; erlang_msg msg; int arity; int version; int i; int needlink; int needmsg; int msglen; /* set up rpc arguments */ /* { PidFrom, { call, Mod, Fun, Args, user }} */ ei_encode_version(buf,&index); ei_encode_tuple_header(buf,&index,2); ei_encode_pid(buf,&index,self); /* PidFrom */ ei_encode_tuple_header(buf,&index,5); ei_encode_atom(buf,&index,"call"); /* call */ ei_encode_atom(buf,&index,EI_MNESIA_MODULE); /* Mod */ ei_encode_atom(buf,&index,EI_MNESIA_RESTORE); /* Fun */ ei_encode_list_header(buf,&index,2); /* Args: [ table, self() ] */ ei_encode_atom(buf,&index,mntab); ei_encode_pid(buf,&index,self); ei_encode_empty_list(buf,&index); ei_encode_atom(buf,&index,"user"); /* user */ /* make the rpc call */ if (ei_send_reg_encoded(fd,self,"rex",buf,index)) return -1; /* get the reply: expect link and message (not sure which will come first though) */ needlink = needmsg = 1; while (needlink || needmsg) { /* get message */ index = EISMALLBUF; while (!(i = ei_recv_internal(fd,&bufp,&index,&msg,&msglen,1,0))) index = EISMALLBUF; switch (i) { case ERL_LINK: /* got link */ if (!needlink) return -1; needlink = 0; break; case ERL_SEND: /* got message - is it the right one? */ if (!needmsg) return -1; else { /* expecting { rex, { size, Pid, Count, MaxKey, MaxObj }} */ index = 0; if (ei_decode_version(buf,&index,&version) || ei_decode_tuple_header(buf,&index,&arity) || (arity != 2) || ei_decode_atom(buf,&index,tmpbuf) || strcmp(tmpbuf,"rex") || ei_decode_tuple_header(buf,&index,&arity) || (arity != 5) || ei_decode_atom(buf,&index,tmpbuf) || strcmp(tmpbuf,EI_MNESIA_SIZE) || ei_decode_pid(buf,&index,mnesia) || ei_decode_long(buf,&index,count) || ei_decode_long(buf,&index,maxkey) || ei_decode_long(buf,&index,maxobj)) return -1; /* bad response from other side */ /* got msg */ needmsg = 0; } break; default: return -1; /* wasn't link or pid */ } } return 0; } static int mn_unlink(int fd) { erlang_msg msg; char buf[EISMALLBUF]; char *bufp=buf; int index; int msglen; /* wait for unlink or exit */ while (1) { index = EISMALLBUF; switch (ei_recv_internal(fd,&bufp,&index,&msg,&msglen,1,0)) { case 0: continue; case ERL_UNLINK: return 0; default: return -1; } } return 0; } /* decode an object and insert it into the table */ static int mn_decode_insert(ei_reg *reg, const char *msgbuf, int *index, char *key) { long keylen; long objlen; long objtype; void *objbuf = NULL; long i; double f; if (ei_decode_long(msgbuf,index,&keylen) || ei_decode_long(msgbuf,index,&objlen) || ei_decode_long(msgbuf,index,&objtype)) return -1; /* decode key */ if (ei_decode_string(msgbuf,index,key)) { if (objbuf) free(objbuf); return -1; } /* finally! decode object and insert in table */ /* don't forget to fix attributes (dirty bit for example) */ /* FIXME: added cast but 64 bit trouble I think */ switch ((int)objtype & EI_REG_TYPEMASK) { case EI_INT: if (ei_decode_long(msgbuf,index,&i)) return -1; ei_reg_setival(reg,key,i); break; case EI_FLT: if (ei_decode_double(msgbuf,index,&f)) return -1; ei_reg_setfval(reg,key,f); break; case EI_STR: objbuf = NULL; if (objlen > 0) { if (!(objbuf = malloc(objlen))) return -1; if (ei_decode_string(msgbuf,index,objbuf)) { free(objbuf); return -1; } ei_reg_setsval(reg,key,objbuf); } else { /* just a pointer to nothing */ if (ei_decode_long(msgbuf,index,&i)) return -1; ei_reg_setsval(reg,key,NULL); } break; case EI_BIN: objbuf = NULL; if (objlen > 0) { if (!(objbuf = malloc(objlen))) return -1; if (ei_decode_binary(msgbuf,index,objbuf,&i)) { free(objbuf); return -1; } /* assert(i == objlen) */ ei_reg_setpval(reg,key,objbuf,objlen); } else { /* just a pointer to nothing */ if (ei_decode_long(msgbuf,index,&i)) return -1; ei_reg_setpval(reg,key,(void *)i,0); } break; default: /* unknown type */ if (objbuf) free(objbuf); return -1; } /* switch */ return 0; } /* help function passed to hash_foreach, to clear dirty bits */ /* use after successful restore */ static int clean_obj(const char *key, const void *p) { ei_reg_obj *obj = (ei_reg_obj *)p; if (obj) obj->attr &= ~EI_DIRTY; return 0; } int ei_reg_restore(int fd, ei_reg *reg, const char *mntab) { int i,j; char tag[32]; char sbuf[EISMALLBUF]; char *dbuf = NULL; char *msgbuf = NULL; char *keybuf = NULL; erlang_pid self; erlang_pid mnesia = {"",0,0,0}; erlang_msg msg; int index = 0; int len = 0; int msglen; int version = 0; int arity = 0; long count = 0; long maxkey = 0; long maxobj = 0; ei_cnode *ec; if (!reg || !mntab) return -1; /* return EI_BADARG; */ /* make a self pid */ if ((ec = ei_fd_to_cnode(fd)) == NULL) { return -1; } strcpy(self.node,ei_thisnodename(ec)); self.num = fd; self.serial = 0; self.creation = ei_thiscreation(ec); if (mn_start_restore(fd,&self,&mnesia,mntab,&count,&maxkey,&maxobj)) { /* send exit *only* if we have pid */ if (mnesia.node[0]) ei_send_exit(fd,&self,&mnesia,"bad response from rpc start"); return -1; } if (count <= 0) { ei_send_exit(fd,&self,&mnesia,"nothing to do"); return 0; } /* make sure receive buffer can handle largest expected message */ len = maxkey + maxobj + 512; if (len > EISMALLBUF) if (!(dbuf = malloc(len))) { ei_send_exit(fd,&self,&mnesia,"cannot allocate space for incoming data"); return -1; } msgbuf = (dbuf ? dbuf : sbuf); /* allocate space for largest key */ if (!(keybuf = malloc(maxkey+1))) goto restore_failure; /* get this ball rolling */ index = 0; ei_encode_version(msgbuf,&index); ei_encode_tuple_header(msgbuf,&index,2); ei_encode_atom(msgbuf,&index,"send_records"); ei_encode_pid(msgbuf,&index,&self); if (ei_send_encoded(fd,&mnesia,msgbuf,index)) goto restore_failure; /* read as much as possible, until count or EXIT */ for (i=0; itab,clean_obj); /* success */ return 0; restore_failure: ei_send_exit(fd,&self,&mnesia,"restore failure"); if (keybuf) free(keybuf); if (dbuf) free(dbuf); return -1; }