static int put_shmdata(int id) { struct shm_data *data; int ret = -1; SYSV_MUTEX_LOCK(&lock_resources); data = _hash_lookup(shmres, id); if (!data) { sysv_print_err("something wrong put_shmdata\n"); goto done; /* It should not reach here. */ } data->used--; if (data->used == 0 && data->removed) { sysv_print("really remove the sem\n"); SYSV_MUTEX_UNLOCK(&lock_resources); /* OBS: Even if the shmctl fails (the thread doesn't * have IPC_M permissions), all structures associated * with it will be removed in the current process.*/ sysvipc_shmdt(data->internal); semundo_clear(id, -1); if (data->removed == SEG_ALREADY_REMOVED) return 1; /* The semaphore was removed by another process so there is nothing else we must do. */ /* Else inform the daemon that the segment is removed. */ return (sysvipc_shmctl(id, IPC_RMID, NULL)); } ret = 0; done: SYSV_MUTEX_UNLOCK(&lock_resources); return (ret); }
/* Handle a shmdt() request. */ int handle_shmdt(pid_t pid, int shmid) { struct shmid_ds *shmseg; int segnum; struct shm_handle *handle; struct pid_attached *pidatt; struct id_attached *idatt; struct client *cl; sysvd_print("shmdt pid %d shmid %d\n", pid, shmid); /*if (!jail_sysvipc_allowed && td->td_cmsgcred->cr_prison != NULL) return (ENOSYS); */ segnum = IPCID_TO_IX(shmid); shmseg = &shmsegs[segnum]; handle = shmseg->shm_internal; /* Check if pid is attached. */ LIST_FOREACH(pidatt, &handle->attached_list, link) if (pidatt->pid == pid) break; if (!pidatt) { sysvd_print_err("process %d is not attached to %d (1)\n", pid, shmid); return (EINVAL); } LIST_REMOVE(pidatt, link); /* Remove the segment from the list of attached segments of the pid.*/ cl = _hash_lookup(clientshash, pid); LIST_FOREACH(idatt, &cl->ids_attached, link) if (idatt->shmid == shmid) break; if (!idatt) { sysvd_print_err("process %d is not attached to %d (2)\n", pid, shmid); return (EINVAL); } LIST_REMOVE(idatt, link); shmseg->shm_dtime = time(NULL); /* If no other process attaced remove the segment. */ if ((--shmseg->shm_nattch <= 0) && (shmseg->shm_perm.mode & SHMSEG_REMOVED)) { shm_deallocate_segment(segnum); shm_last_free = segnum; } return (0); }
/* Install for the client the file corresponding to fd. */ static int install_fd_client(pid_t pid, int fd) { int ret; struct client *cl = _hash_lookup(clientshash, pid); if (!cl) { sysvd_print_err("no client entry for pid = %d\n", pid); return (-1); } ret = send_fd(cl->sock, fd); if (ret < 0) { sysvd_print_err("can not send fd to client %d\n", pid); return (-1); } return (0); }
/* add into hash table without checking for repeats */ int hash_remove(T_HashTable *H,T_HashTableEl *E, int hint) { T_HashTableEl *E2; if (hint >=0 && hint < H->size && H->entries[hint] == E){ H->inuse--; H->entries[hint] = &deleted; return 0; } if(_hash_lookup(H, E, &E2, &hint, 1)) { fprintf(stderr, "Removing non-existent entry\n"); exit(1); return -1; } H->inuse--; H->entries[hint] = &deleted; return 0; }
int hash_lookup(T_HashTable *H,T_HashTableEl *E, T_HashTableEl **E2, int *hint) { return _hash_lookup(H, E, E2, hint, 0); }
/* * Adjust a particular entry for a particular proc */ static int semundo_adjust(int semid, int semnum, int adjval) { struct undo *sunptr; int i; int error = 0; size_t size; int undoid; void *addr; struct shm_data *data; sysv_print("semundo adjust\n"); if (!adjval) goto done; SYSV_MUTEX_LOCK(&lock_undo); if (!undos) { sysv_print("get undo segment\n"); undoid = _shmget(IPC_PRIVATE, PAGE_SIZE, IPC_CREAT | IPC_EXCL | 0600, UNDOGET); if (undoid == -1) { sysv_print_err("no undo segment\n"); return (-1); } addr = sysvipc_shmat(undoid, NULL, 0); if (!addr) { sysv_print_err("can not map undo segment\n"); sysvipc_shmctl(undoid, IPC_RMID, NULL); return (-1); } undos = (struct sem_undo *)addr; undos->un_pages = 1; undos->un_cnt = 0; } /* * Look for the requested entry and adjust it (delete if adjval becomes * 0). */ sunptr = &undos->un_ent[0]; for (i = 0; i < undos->un_cnt; i++, sunptr++) { if (sunptr->un_id != semid && sunptr->un_num != semnum) continue; sunptr->un_adjval += adjval; if (sunptr->un_adjval == 0) { undos->un_cnt--; if (i < undos->un_cnt) undos->un_ent[i] = undos->un_ent[undos->un_cnt]; } goto done; } /* Didn't find the right entry - create it */ size = sizeof(struct sem_undo) + (undos->un_cnt + 1) * sizeof(struct sem_undo); if (size > (unsigned int)(undos->un_pages * PAGE_SIZE)) { sysv_print("need more undo space\n"); sysvipc_shmdt(undos); undos->un_pages++; SYSV_MUTEX_LOCK(&lock_resources); data = _hash_lookup(shmaddrs, (u_long)undos); SYSV_MUTEX_UNLOCK(&lock_resources); /* It is not necessary any lock on "size" because it is used * only by shmat and shmdt. * shmat for undoid is called only from this function and it * is protected by undo_lock. * shmdt for undoid is not called anywhere because the segment * is destroyed by the daemon when the client dies. */ data->size = undos->un_pages * PAGE_SIZE; undos = sysvipc_shmat(data->shmid, NULL, 0); } sunptr = &undos->un_ent[undos->un_cnt]; undos->un_cnt++; sunptr->un_adjval = adjval; sunptr->un_id = semid; sunptr->un_num = semnum; //if (suptr->un_cnt == seminfo.semume) TODO move it in daemon /*} else { error = EINVAL; //se face prin notificare }*/ done: SYSV_MUTEX_UNLOCK(&lock_undo); sysv_print("semundo adjust end\n"); return (error); }
/* Handle a shmat() request. */ int handle_shmat(pid_t pid, struct shmat_msg *shmat_msg, struct cmsgcred *cred ) { int error; int fd; struct shmid_ds *shmseg; struct pid_attached *pidatt; struct shm_handle *handle; size_t new_size = shmat_msg->size; struct client *cl; struct id_attached *idatt; /*if (!jail_sysvipc_allowed && td->td_cmsgcred->cr_prison != NULL) return (ENOSYS); again:*/ shmseg = shm_find_segment_by_shmid(shmat_msg->shmid); if (shmseg == NULL) { sysvd_print_err("shmat error: segment was not found\n"); error = EINVAL; goto done; } error = ipcperm(cred, &shmseg->shm_perm, (shmat_msg->shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W); if (error) goto done; handle = shmseg->shm_internal; if (shmat_msg->size > shmseg->shm_segsz) { if (handle->type != UNDOGET) { error = EINVAL; goto done; } fd = ((struct shm_handle*)shmseg->shm_internal)->fd; ftruncate(fd, round_page(new_size)); shmseg->shm_segsz = new_size; } shmseg->shm_lpid = pid; shmseg->shm_atime = time(NULL); if (handle->type != UNDOGET) shmseg->shm_nattch++; else shmseg->shm_nattch = 1; /* Only a process calls shmat and only once. If it does it for more than once that is because it called exec() and reinitialized the undo segment. */ /* Insert the pid in the segment list of attaced pids. * The list is checked in handle_shmdt so that only * attached pids can dettached from this segment. */ sysvd_print("nattch = %d pid = %d\n", shmseg->shm_nattch, pid); pidatt = malloc(sizeof(*pidatt)); pidatt->pid = pid; LIST_INSERT_HEAD(&handle->attached_list, pidatt, link); /* Add the segment at the list of attached segments of the client. * It is used when the process finishes its execution. The daemon * walks through the list to dettach the segments. */ idatt = malloc(sizeof(*idatt)); idatt->shmid = shmat_msg->shmid; cl = _hash_lookup(clientshash, pid); LIST_INSERT_HEAD(&cl->ids_attached, idatt, link); return (0); done: return (error); }
/* Create a shared memory segment and return the id. */ static int shmget_allocate_segment(pid_t pid, struct shmget_msg *shmget_msg, int mode, struct cmsgcred *cred) { int i, segnum, shmid; size_t size; struct shmid_ds *shmseg; struct shm_handle *handle; #if 0 /* It is possible after a process calls exec(). * We don't create another segment but return the old one * with all information. * This segment is destroyed only when process dies. * */ if (shmget_msg->type == UNDOGET) { struct client *cl= _hash_lookup(clientshash, pid); if (cl->undoid != -1) return cl->undoid; } #endif if ((long)shmget_msg->size < shminfo.shmmin) //|| (long)shmget_msg->size > shminfo.shmmax) /* There is no need to check the max limit, * the operating system do this for us. */ return (-EINVAL); if (shm_nused >= shminfo.shmmni) /* any shmids left? */ return (-ENOSPC); /* Compute the size of the segment. */ size = round_page(shmget_msg->size); /* Find a free entry in the shmsegs vector. */ if (shm_last_free < 0) { // shmrealloc(); /* maybe expand the shmsegs[] array */ for (i = 0; i < shmalloced; i++) { if (shmsegs[i].shm_perm.mode & SHMSEG_FREE) break; } if (i == shmalloced) { sysvd_print("i == shmalloced\n"); return (-ENOSPC); } segnum = i; } else { segnum = shm_last_free; shm_last_free = -1; } shmseg = &shmsegs[segnum]; /* * In case we sleep in malloc(), mark the segment present but deleted * so that noone else tries to create the same key. */ shmseg->shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED; shmseg->shm_perm.key = shmget_msg->key; shmseg->shm_perm.seq = (shmseg->shm_perm.seq + 1) & 0x7fff; /* Create the file for the shared memory segment. */ handle = shmseg->shm_internal = malloc(sizeof(struct shm_handle)); handle->type = shmget_msg->type; handle->fd = create_sysv_file(shmget_msg, size, shmseg); if (handle->fd == -1) { free(handle); handle = NULL; shmseg->shm_perm.mode = SHMSEG_FREE; shm_last_free = segnum; errno = -ENFILE; return (-1); } LIST_INIT(&handle->attached_list); if (handle->fd < 0) { free(shmseg->shm_internal); shmseg->shm_internal = NULL; shm_last_free = segnum; shmseg->shm_perm.mode = SHMSEG_FREE; return (-errno); } /* Get the id. */ shmid = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cmcred_euid; shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cmcred_gid; shmseg->shm_perm.mode = (shmseg->shm_perm.mode & SHMSEG_WANTED) | (mode & ACCESSPERMS) | SHMSEG_ALLOCATED; shmseg->shm_cpid = pid; shmseg->shm_lpid = shmseg->shm_nattch = 0; shmseg->shm_atime = shmseg->shm_dtime = 0; shmseg->shm_ctime = time(NULL); shmseg->shm_segsz = shmget_msg->size; shm_committed += btoc(size); shm_nused++; if (shmseg->shm_perm.mode & SHMSEG_WANTED) { /* * Somebody else wanted this key while we were asleep. Wake * them up now. */ shmseg->shm_perm.mode &= ~SHMSEG_WANTED; //TODO multithreading //wakeup((caddr_t)shmseg); } shmseg->shm_perm.mode &= ~SHMSEG_REMOVED; if (shmget_msg->type == UNDOGET) { /* The file is used by daemon when clients terminates * and sem_undo resources must be cleaned. */ struct client *cl= _hash_lookup(clientshash, pid); cl->undoid = shmid; } return (shmid); }