/* $Id: shbuf.c,v 1.2 2002/05/02 16:16:30 poettering Exp $ * * This file is part of libshbuf. * * asd is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your * option) any later version. * * asd is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with libshbuf; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include #include #include #include #include #include #include #include #include "shbuf.h" #include "internal.h" #include "thread.h" #include "shbuferr.h" shbuf* shbuf_create(key_t key, unsigned long size) { shbuf *sb; if (key == 0) { int i; for (i = 4711; i < 4711+5000; i++) if ((sb = shbuf_create(i, size))) return sb; return NULL; } sb = malloc(sizeof(shbuf)); assert(sb); // Create control SHM if ((sb->control_shmid = shmget((sb->control_shm_key = key), sizeof(shbuf_control), IPC_CREAT | IPC_EXCL | 0700)) >= 0) { // Map control SHM sb->control = (shbuf_control*) shmat(sb->control_shmid, NULL, 0); if (sb->control && sb->control != (shbuf_control*) -1) { // Create SEM if ((sb->semid = semget((sb->control->sem_key = key+1), 1, IPC_CREAT | IPC_EXCL | 0700)) >= 0) { // Reset SEM union semun arg; arg.val = 1; if (semctl(sb->semid, 0, SETVAL, arg) >= 0) { // Create buffer SHM if ((sb->buffer_shmid = shmget((sb->control->buffer_shm_key = key+2), size, IPC_CREAT | IPC_EXCL | 0700)) >= 0) { // Map buffer SHM sb->buffer = (unsigned char*) shmat(sb->buffer_shmid, NULL, 0); if (sb->buffer && sb->buffer != (unsigned char*) -1) { // Create MSG if ((sb->msgid = msgget((sb->control->msg_key = key+3), IPC_CREAT | IPC_EXCL | 0700)) >= 0) { sb->control->id = SHBUF_CONTROL_ID; sb->control->version = SHBUF_CONTROL_VERSION; sb->control->client_attached = !(sb->control->provider_attached = 1); sb->control->size = size; sb->control->provider_notify = sb->control->client_notify = 0; sb->control->status.read_idx = sb->control->status.length = 0; sb->control->status.write_count = sb->control->status.read_count = 0; sb->control->status.ignore_read_inc = sb->control->status.ignore_write_inc = 0; sb->control->status.backlog = sb->control->status.backlog_target = 0; sb->is_dead = !(sb->is_provider = 1); sb->fifo_fd_read = sb->fifo_fd_write = -1; sb->thread = (pthread_t) 0; return sb; } else shbuf_set_errno(SHBUF_COULDNOTCREATEMSGQ); shmdt(sb->control); } else shbuf_set_errno(SHBUF_COULDNOTMAPBUFFERSHM); shmctl(sb->buffer_shmid, IPC_RMID, NULL); } else shbuf_set_errno(SHBUF_COULDNOTCREATEBUFFERSHM); } else shbuf_set_errno(SHBUF_COULDNOTRESETSEM); semctl(sb->semid, 0, IPC_RMID, NULL); } else shbuf_set_errno(SHBUF_COULDNOTCREATESEM); shmdt(sb->control); } else shbuf_set_errno(SHBUF_COULDNOTMAPCONTROLSHM); shmctl(sb->control_shmid, IPC_RMID, NULL); } else shbuf_set_errno(SHBUF_COULDNOTCREATECONTROLSHM); free(sb); return NULL; } shbuf* shbuf_open(key_t key) { shbuf *sb; sb = malloc(sizeof(shbuf)); assert(sb); if (key == 0) return NULL; // Get control SHM if ((sb->control_shmid = shmget((sb->control_shm_key = key), sizeof(shbuf_control), 0)) >= 0) { // Map control SHM sb->control = (shbuf_control*) shmat(sb->control_shmid, NULL, 0); if (sb->control && sb->control != (shbuf_control*) -1) { // Check signature if (sb->control->id == SHBUF_CONTROL_ID && sb->control->version == SHBUF_CONTROL_VERSION) { // Get SEM if ((sb->semid = semget(sb->control->sem_key, 1, 0)) >= 0) { // Get buffer SHM if ((sb->buffer_shmid = shmget(sb->control->buffer_shm_key, 0, 0)) >= 0) { // Map buffer SHM sb->buffer = (unsigned char*) shmat(sb->buffer_shmid, NULL, 0); if (sb->buffer && sb->buffer != (unsigned char*) -1) { // Get MSG if ((sb->msgid = msgget(sb->control->msg_key, 0)) >= 0) { int quit = 0; // Check for exclusive access shbuf_status_lock(sb); if (!sb->control->provider_attached || sb->control->client_attached) quit = 1; else { sb->control->client_attached = 1; sb->control->client_notify = 0; } shbuf_status_unlock(sb); if (!quit) { sb->is_dead = sb->is_provider = 0; sb->fifo_fd_read = sb->fifo_fd_write = -1; sb->thread = (pthread_t) 0; return sb; } else shbuf_set_errno(SHBUF_BUSY); } else shbuf_set_errno(SHBUF_COULDNOTOPENMSGQ); shmdt(sb->buffer); } else shbuf_set_errno(SHBUF_COULDNOTMAPBUFFERSHM); } else shbuf_set_errno(SHBUF_COULDNOTOPENBUFFERSHM); } else shbuf_set_errno(SHBUF_COULDNOTOPENSEM); } else shbuf_set_errno(SHBUF_INCOMPATIBLEBUFFER); shmdt(sb->control); } else shbuf_set_errno(SHBUF_COULDNOTMAPCONTROLSHM); } else shbuf_set_errno(SHBUF_COULDNOTOPENCONTROLSHM); free(sb); return NULL; } void shbuf_free(shbuf* sb) { assert(sb); sb->is_dead = 1; if (sb->is_provider) sb->control->provider_attached = 0; else sb->control->client_attached = 0; shbuf_notify(sb); thread_stop(sb); shmdt(sb->control); shmdt(sb->buffer); if (sb->is_provider) { shmctl(sb->control_shmid, IPC_RMID, NULL); shmctl(sb->buffer_shmid, IPC_RMID, NULL); semctl(sb->semid, 0, IPC_RMID, NULL); msgctl(sb->msgid, IPC_RMID, NULL); } free(sb); } static int _sem_access(int semid, int mode) { union semun arg; struct semid_ds ds; arg.buf = &ds; if (semctl(semid, 0, IPC_STAT, arg) < 0) return -1; arg.buf->sem_perm.mode = mode & 1023; return semctl(semid, 0, IPC_SET, arg); } static int _shm_access(int shmid, int mode) { struct shmid_ds ds; if (shmctl(shmid, IPC_STAT, &ds) < 0) return -1; ds.shm_perm.mode = mode & 1023; return shmctl(shmid, IPC_SET, &ds); } static int _msg_access(int msgid, int mode) { struct msqid_ds ds; if (msgctl(msgid, IPC_STAT, &ds) < 0) return -1; ds.msg_perm.mode = mode & 1023; return msgctl(msgid, IPC_SET, &ds); } int shbuf_access(shbuf* sb, int mode) { assert(sb); if ((_shm_access(sb->control_shmid, mode) < 0) || (_sem_access(sb->semid, mode) < 0) || (_shm_access(sb->buffer_shmid, mode) < 0) || (_msg_access(sb->msgid, mode) < 0)) { shbuf_set_errno(SHBUF_ACCESSMODEFAILED); return -1; } return 0; } key_t shbuf_get_key(shbuf *sb) { assert(sb); return sb->control_shm_key; } unsigned long shbuf_get_size(shbuf *sb) { assert(sb); return sb->control->size; } unsigned char* shbuf_get_pointer(shbuf *sb) { assert(sb); return sb->buffer; } shbuf_status* shbuf_get_status(shbuf *sb) { assert(sb); return &sb->control->status; } int shbuf_notify(shbuf *sb) { struct { long mtype; char mtext[1]; } msgbuf; assert(sb); msgbuf.mtype = sb->is_provider ? 2 : 1; msgbuf.mtext[0] = 'X'; if (msgsnd(sb->msgid, &msgbuf, 1, IPC_NOWAIT) == -1) if (errno != EAGAIN) { shbuf_set_errno(SHBUF_MSGSNDFAILED); return -1; } return 0; } int shbuf_wait(shbuf *sb) { fd_set fds; assert(sb); if (sb->thread == (pthread_t) 0) { shbuf_set_errno(SHBUF_NOTINNOTIFYMODE); return -1; } FD_ZERO(&fds); FD_SET(sb->fifo_fd_read, &fds); if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) != 1) { shbuf_set_errno(SHBUF_SELECTFAILED); return -1; } return shbuf_post_select(sb); } int shbuf_get_select_fd(shbuf* sb) { assert(sb); if (sb->thread == (pthread_t) 0) { shbuf_set_errno(SHBUF_NOTINNOTIFYMODE); return -1; } return sb->fifo_fd_read; } int shbuf_post_select(shbuf *sb) { static char foo[200]; assert(sb); if (sb->thread == (pthread_t) 0) { shbuf_set_errno(SHBUF_NOTINNOTIFYMODE); return -1; } while (read(sb->fifo_fd_read, foo, sizeof(foo)) > 0); if (errno == EAGAIN) return 0; shbuf_set_errno(SHBUF_READFAILED); return -1; }