removed the static return values in the uid and gid functions. This is all done without cig
This commit is contained in:
parent
c2ccd7c924
commit
bce5c62cbf
|
@ -165,8 +165,8 @@ CIGGEDINIT = $(patsubst %,s48_init_%, $(CIGGED))
|
|||
|
||||
EXTERNAL_OBJECTS = $(SOCKET_OBJECTS) $(LOOKUP_OBJECTS)
|
||||
EXTERNAL_FLAGS = $(SOCKET_FLAGS)
|
||||
EXTERNAL_INITIALIZERS = $(SOCKET_INITIALIZERS) $(LOOKUP_INITIALIZERS) s48_init_cig \
|
||||
$(CIGGEDINIT)
|
||||
EXTERNAL_INITIALIZERS = $(SOCKET_INITIALIZERS) $(LOOKUP_INITIALIZERS) \
|
||||
s48_init_cig $(CIGGEDINIT) s48_init_userinfo
|
||||
|
||||
|
||||
# Rules for any external code.
|
||||
|
|
131
scsh/syscalls.c
131
scsh/syscalls.c
|
@ -264,23 +264,6 @@ s48_value df_setuid(s48_value g1)
|
|||
return ret1;
|
||||
}
|
||||
|
||||
s48_value df_my_username(s48_value mv_vec)
|
||||
{
|
||||
extern char *my_username(void);
|
||||
s48_value ret1;
|
||||
S48_DECLARE_GC_PROTECT(2);
|
||||
char *r1;
|
||||
|
||||
|
||||
|
||||
S48_GC_PROTECT_2(mv_vec,ret1);
|
||||
r1 = my_username();
|
||||
ret1 = S48_VECTOR_REF(mv_vec,0);
|
||||
SetAlienVal(S48_CAR(ret1),(long) r1); S48_SET_CDR(ret1,strlen_or_false(r1));//str-and-len
|
||||
S48_GC_UNPROTECT();
|
||||
return ret1;
|
||||
}
|
||||
|
||||
s48_value df_getpid(void)
|
||||
{
|
||||
extern pid_t getpid(void);
|
||||
|
@ -960,114 +943,6 @@ s48_value df_pause(void)
|
|||
return S48_FALSE;
|
||||
}
|
||||
|
||||
s48_value df_alarm(s48_value g1)
|
||||
{
|
||||
extern unsigned int alarm(unsigned int );
|
||||
s48_value ret1;
|
||||
S48_DECLARE_GC_PROTECT(1);
|
||||
unsigned int r1;
|
||||
|
||||
|
||||
|
||||
S48_GC_PROTECT_1(ret1);
|
||||
r1 = alarm(s48_extract_integer(g1));
|
||||
ret1 = s48_enter_fixnum(r1);
|
||||
S48_GC_UNPROTECT();
|
||||
return ret1;
|
||||
}
|
||||
|
||||
s48_value df_user_info_uid(s48_value g1, s48_value mv_vec)
|
||||
{
|
||||
extern int user_info_uid(uid_t , char **, gid_t *, char **, char **);
|
||||
s48_value ret1;
|
||||
S48_DECLARE_GC_PROTECT(2);
|
||||
int r1;
|
||||
char *r2;
|
||||
gid_t r3;
|
||||
char *r4;
|
||||
char *r5;
|
||||
|
||||
|
||||
|
||||
S48_GC_PROTECT_2(mv_vec,ret1);
|
||||
r1 = user_info_uid(s48_extract_fixnum(g1), &r2, &r3, &r4, &r5);
|
||||
ret1 = ENTER_BOOLEAN(r1);
|
||||
SetAlienVal(S48_CAR(S48_VECTOR_REF(mv_vec,0)),(long) r2); S48_SET_CDR(S48_VECTOR_REF(mv_vec,0),strlen_or_false(r2));//str-and-len
|
||||
S48_VECTOR_SET(mv_vec,1,s48_enter_fixnum(r3));
|
||||
SetAlienVal(S48_CAR(S48_VECTOR_REF(mv_vec,2)),(long) r4); S48_SET_CDR(S48_VECTOR_REF(mv_vec,2),strlen_or_false(r4));//str-and-len
|
||||
SetAlienVal(S48_CAR(S48_VECTOR_REF(mv_vec,3)),(long) r5); S48_SET_CDR(S48_VECTOR_REF(mv_vec,3),strlen_or_false(r5));//str-and-len
|
||||
S48_GC_UNPROTECT();
|
||||
return ret1;
|
||||
}
|
||||
|
||||
s48_value df_user_info_name(s48_value g1, s48_value mv_vec)
|
||||
{
|
||||
extern int user_info_name(const char *, uid_t *, gid_t *, char **, char **);
|
||||
s48_value ret1;
|
||||
S48_DECLARE_GC_PROTECT(2);
|
||||
int r1;
|
||||
uid_t r2;
|
||||
gid_t r3;
|
||||
char *r4;
|
||||
char *r5;
|
||||
|
||||
|
||||
|
||||
S48_GC_PROTECT_2(mv_vec,ret1);
|
||||
r1 = user_info_name(s48_extract_string(g1), &r2, &r3, &r4, &r5);
|
||||
ret1 = ENTER_BOOLEAN(r1);
|
||||
S48_VECTOR_SET(mv_vec,0,s48_enter_fixnum(r2));
|
||||
S48_VECTOR_SET(mv_vec,1,s48_enter_fixnum(r3));
|
||||
SetAlienVal(S48_CAR(S48_VECTOR_REF(mv_vec,2)),(long) r4); S48_SET_CDR(S48_VECTOR_REF(mv_vec,2),strlen_or_false(r4));//str-and-len
|
||||
SetAlienVal(S48_CAR(S48_VECTOR_REF(mv_vec,3)),(long) r5); S48_SET_CDR(S48_VECTOR_REF(mv_vec,3),strlen_or_false(r5));//str-and-len
|
||||
S48_GC_UNPROTECT();
|
||||
return ret1;
|
||||
}
|
||||
|
||||
s48_value df_group_info_gid(s48_value g1, s48_value mv_vec)
|
||||
{
|
||||
extern int group_info_gid(int , char **, char** *, int *);
|
||||
s48_value ret1;
|
||||
S48_DECLARE_GC_PROTECT(2);
|
||||
int r1;
|
||||
char *r2;
|
||||
char** r3;
|
||||
int r4;
|
||||
|
||||
|
||||
|
||||
S48_GC_PROTECT_2(mv_vec,ret1);
|
||||
r1 = group_info_gid(s48_extract_integer(g1), &r2, &r3, &r4);
|
||||
ret1 = ENTER_BOOLEAN(r1);
|
||||
SetAlienVal(S48_CAR(S48_VECTOR_REF(mv_vec,0)),(long) r2); S48_SET_CDR(S48_VECTOR_REF(mv_vec,0),strlen_or_false(r2));//str-and-len
|
||||
SetAlienVal(S48_VECTOR_REF(mv_vec,1),(long) r3);//simple-assign
|
||||
S48_VECTOR_SET(mv_vec,2,s48_enter_fixnum(r4));
|
||||
S48_GC_UNPROTECT();
|
||||
return ret1;
|
||||
}
|
||||
|
||||
s48_value df_group_info_name(s48_value g1, s48_value mv_vec)
|
||||
{
|
||||
extern int group_info_name(const char *, int *, char** *, int *);
|
||||
s48_value ret1;
|
||||
S48_DECLARE_GC_PROTECT(2);
|
||||
int r1;
|
||||
int r2;
|
||||
char** r3;
|
||||
int r4;
|
||||
|
||||
|
||||
|
||||
S48_GC_PROTECT_2(mv_vec,ret1);
|
||||
r1 = group_info_name(s48_extract_string(g1), &r2, &r3, &r4);
|
||||
ret1 = ENTER_BOOLEAN(r1);
|
||||
S48_VECTOR_SET(mv_vec,0,s48_enter_integer(r2));
|
||||
SetAlienVal(S48_VECTOR_REF(mv_vec,1),(long) r3);//simple-assign
|
||||
S48_VECTOR_SET(mv_vec,2,s48_enter_fixnum(r4));
|
||||
S48_GC_UNPROTECT();
|
||||
return ret1;
|
||||
}
|
||||
|
||||
s48_value df_open_dir(s48_value g1, s48_value mv_vec)
|
||||
{
|
||||
extern int open_dir(const char *, char** *, int *);
|
||||
|
@ -1334,7 +1209,6 @@ s48_value s48_init_syscalls(void)
|
|||
S48_EXPORT_FUNCTION(df_getuid);
|
||||
S48_EXPORT_FUNCTION(df_geteuid);
|
||||
S48_EXPORT_FUNCTION(df_setuid);
|
||||
S48_EXPORT_FUNCTION(df_my_username);
|
||||
S48_EXPORT_FUNCTION(df_getpid);
|
||||
S48_EXPORT_FUNCTION(df_getppid);
|
||||
S48_EXPORT_FUNCTION(df_getpgrp);
|
||||
|
@ -1377,11 +1251,6 @@ s48_value s48_init_syscalls(void)
|
|||
S48_EXPORT_FUNCTION(df_write_fdes_substring);
|
||||
S48_EXPORT_FUNCTION(df_kill);
|
||||
S48_EXPORT_FUNCTION(df_pause);
|
||||
S48_EXPORT_FUNCTION(df_alarm);
|
||||
S48_EXPORT_FUNCTION(df_user_info_uid);
|
||||
S48_EXPORT_FUNCTION(df_user_info_name);
|
||||
S48_EXPORT_FUNCTION(df_group_info_gid);
|
||||
S48_EXPORT_FUNCTION(df_group_info_name);
|
||||
S48_EXPORT_FUNCTION(df_open_dir);
|
||||
S48_EXPORT_FUNCTION(df_scm_sort_filevec);
|
||||
S48_EXPORT_FUNCTION(df_scm_envvec);
|
||||
|
|
|
@ -202,9 +202,11 @@
|
|||
|
||||
(define-errno-syscall (set-uid uid_t) set-uid/errno)
|
||||
|
||||
(define-foreign %user-login-name (my_username)
|
||||
static-string)
|
||||
|
||||
;;(define-foreign %user-login-name (my_username)
|
||||
;; static-string)
|
||||
|
||||
(import-lambda-definition %user-login-name () "my_username")
|
||||
|
||||
(define (user-login-name)
|
||||
(or (%user-login-name)
|
||||
(error "Cannot get your name")))
|
||||
|
@ -702,7 +704,7 @@
|
|||
|
||||
(define-foreign pause-until-interrupt (pause) no-declare ignore)
|
||||
|
||||
(define-foreign itimer (alarm (uint_t secs)) uint_t)
|
||||
;;; now in low-interrupt: (define-foreign itimer (alarm (uint_t secs)) uint_t)
|
||||
|
||||
;;; User info
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -714,30 +716,28 @@
|
|||
((disclose ui)
|
||||
(list "user-info" (user-info:name ui))))
|
||||
|
||||
(define-foreign %uid->user-info (user_info_uid (uid_t uid))
|
||||
bool ; win?
|
||||
static-string ; name
|
||||
gid_t ; gid
|
||||
static-string ; home-dir
|
||||
static-string); shell
|
||||
|
||||
(define-foreign %name->user-info (user_info_name (string name))
|
||||
bool ; win?
|
||||
uid_t ; uid
|
||||
gid_t ; gid
|
||||
static-string ; home-dir
|
||||
static-string); shell
|
||||
(import-lambda-definition
|
||||
%uid->user-info
|
||||
(uid user-info-record)
|
||||
"user_info_uid")
|
||||
|
||||
(import-lambda-definition
|
||||
%name->user-info
|
||||
(name user-info-record)
|
||||
"user_info_name")
|
||||
|
||||
(define (uid->user-info uid)
|
||||
(receive (win? name gid home-dir shell)
|
||||
(%uid->user-info uid)
|
||||
(if win? (make-user-info name uid gid home-dir shell)
|
||||
(error "Cannot get user's information" uid->user-info uid))))
|
||||
(let ((empty-user-info (make-user-info #f uid #f #f #f)))
|
||||
(if (%uid->user-info uid empty-user-info)
|
||||
empty-user-info
|
||||
(error "Cannot get user's information" uid->user-info uid))))
|
||||
|
||||
|
||||
(define (name->user-info name)
|
||||
(receive (win? uid gid home-dir shell)
|
||||
(%name->user-info name)
|
||||
(if win? (make-user-info name uid gid home-dir shell)
|
||||
(let ((empty-user-info (make-user-info name #f #f #f #f)))
|
||||
(if (%name->user-info name empty-user-info)
|
||||
empty-user-info
|
||||
(error "Cannot get user's information" name->user-info name))))
|
||||
|
||||
(define (user-info uid/name)
|
||||
|
@ -767,37 +767,26 @@
|
|||
;; Make group-info records print like #{group-info wheel}.
|
||||
((disclose gi) (list "group-info" (group-info:name gi))))
|
||||
|
||||
;;; These guys return static structs, so they aren't reentrant.
|
||||
;;; Must be fixed for threaded version.
|
||||
(import-lambda-definition
|
||||
%gid->group-info
|
||||
(gid group-info-record)
|
||||
"group_info_gid")
|
||||
|
||||
(define-foreign %gid->group-info
|
||||
(group_info_gid (integer gid))
|
||||
bool ; win?
|
||||
static-string ; name
|
||||
(C char**) ; members
|
||||
fixnum) ; num members
|
||||
|
||||
(define-foreign %name->group-info
|
||||
(group_info_name (string name))
|
||||
bool ; win?
|
||||
integer ; gid
|
||||
(C char**) ; members
|
||||
fixnum) ; num members
|
||||
(import-lambda-definition
|
||||
%name->group-info
|
||||
(name group-info-record)
|
||||
"group_info_name")
|
||||
|
||||
(define (gid->group-info gid)
|
||||
(receive (win? name members nmembers)
|
||||
(%gid->group-info gid)
|
||||
(if win?
|
||||
(make-group-info name gid
|
||||
(vector->list (C-string-vec->Scheme members nmembers)))
|
||||
(let ((empty-group-info (make-group-info #f gid #f)))
|
||||
(if (%gid->group-info gid empty-group-info)
|
||||
empty-group-info
|
||||
(error "Cannot get group's information for gid" gid))))
|
||||
|
||||
(define (name->group-info name)
|
||||
(receive (win? gid members nmembers)
|
||||
(%name->group-info name)
|
||||
(if win?
|
||||
(make-group-info name gid
|
||||
(vector->list (C-string-vec->Scheme members nmembers)))
|
||||
(let ((empty-group-info (make-group-info name #f #f)))
|
||||
(if (%name->group-info name empty-group-info)
|
||||
empty-group-info
|
||||
(error "Cannot get group's information for name" name))))
|
||||
|
||||
(define (group-info gid/name)
|
||||
|
|
177
scsh/userinfo1.c
177
scsh/userinfo1.c
|
@ -18,19 +18,24 @@
|
|||
#include "userinfo1.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
/* Compute the length of a null-terminated char* vector. */
|
||||
static int strvec_len(char **vec)
|
||||
{
|
||||
int i = 0;
|
||||
char **ptr = vec;
|
||||
if ( !ptr ) return 0;
|
||||
|
||||
while( *ptr++ ) i++;
|
||||
|
||||
return i;
|
||||
#include "scheme48.h"
|
||||
/* build a list from a null-terminated char* vector. */
|
||||
static s48_value char_pp_2_string_list(char **vec){
|
||||
char ** ptr = vec;
|
||||
s48_value list;
|
||||
S48_DECLARE_GC_PROTECT(1);
|
||||
S48_GC_PROTECT_1(list);
|
||||
if (!ptr) {
|
||||
S48_GC_UNPROTECT();
|
||||
return S48_NULL;
|
||||
}
|
||||
|
||||
list = s48_cons (s48_enter_string (*ptr), S48_NULL);
|
||||
while (*(++ptr)){
|
||||
list = s48_cons (s48_enter_string (*ptr), list);
|
||||
}
|
||||
S48_GC_UNPROTECT();
|
||||
return list;
|
||||
}
|
||||
|
||||
/* User db access routines
|
||||
*******************************************************************************
|
||||
|
@ -40,80 +45,114 @@ static int strvec_len(char **vec)
|
|||
** Uses our *real* uid, not our effective one.
|
||||
*/
|
||||
|
||||
char *my_username(void)
|
||||
s48_value my_username(void)
|
||||
{
|
||||
char *s = getlogin();
|
||||
if( s ) return s;
|
||||
if( s ) return s48_enter_string (s);
|
||||
else {
|
||||
struct passwd *pwd = getpwuid(getuid());
|
||||
return pwd ? pwd->pw_name : (char*) 0;
|
||||
return pwd ? s48_enter_string(pwd->pw_name) : S48_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
int user_info_uid(uid_t uid,
|
||||
char **name, gid_t *gid, char **dir, char **shell)
|
||||
s48_value user_info_uid(s48_value scheme_uid, s48_value user_info_record)
|
||||
{
|
||||
struct passwd *pwd = getpwuid(uid);
|
||||
struct passwd *pwd = getpwuid(s48_extract_fixnum(scheme_uid));
|
||||
S48_DECLARE_GC_PROTECT(1);
|
||||
|
||||
S48_GC_PROTECT_1(user_info_record);
|
||||
|
||||
if( !pwd ) {
|
||||
*name = 0;
|
||||
*gid = 0;
|
||||
*dir = 0;
|
||||
*shell = 0;
|
||||
return 0;
|
||||
}
|
||||
*name = pwd->pw_name;
|
||||
*gid = pwd->pw_gid;
|
||||
*dir = pwd->pw_dir;
|
||||
*shell = pwd->pw_shell;
|
||||
return 1;
|
||||
S48_GC_UNPROTECT();
|
||||
return S48_FALSE;
|
||||
}
|
||||
|
||||
int user_info_name(const char *name,
|
||||
uid_t *uid, gid_t *gid, char **dir, char **shell)
|
||||
S48_RECORD_SET(user_info_record, 0, s48_enter_string (pwd->pw_name));
|
||||
S48_RECORD_SET(user_info_record, 2, s48_enter_fixnum (pwd->pw_gid));
|
||||
S48_RECORD_SET(user_info_record, 3, s48_enter_string (pwd->pw_dir));
|
||||
S48_RECORD_SET(user_info_record, 4, s48_enter_string (pwd->pw_shell));
|
||||
S48_GC_UNPROTECT();
|
||||
return S48_TRUE;
|
||||
}
|
||||
|
||||
s48_value user_info_name(s48_value scheme_name, s48_value user_info_record)
|
||||
{
|
||||
struct passwd *pwd = getpwnam(name);
|
||||
struct passwd *pwd = getpwnam(s48_extract_string (scheme_name));
|
||||
S48_DECLARE_GC_PROTECT(1);
|
||||
|
||||
S48_GC_PROTECT_1(user_info_record);
|
||||
|
||||
if( !pwd ) {
|
||||
*uid = 0;
|
||||
*gid = 0;
|
||||
*dir = 0;
|
||||
*shell = 0;
|
||||
return 0;
|
||||
}
|
||||
*uid = pwd->pw_uid;
|
||||
*gid = pwd->pw_gid;
|
||||
*dir = pwd->pw_dir;
|
||||
*shell = pwd->pw_shell;
|
||||
return 1;
|
||||
S48_GC_UNPROTECT();
|
||||
return S48_FALSE;
|
||||
}
|
||||
|
||||
S48_RECORD_SET(user_info_record, 1, s48_enter_fixnum (pwd->pw_uid));
|
||||
S48_RECORD_SET(user_info_record, 2, s48_enter_fixnum (pwd->pw_gid));
|
||||
S48_RECORD_SET(user_info_record, 3, s48_enter_string (pwd->pw_dir));
|
||||
S48_RECORD_SET(user_info_record, 4, s48_enter_string (pwd->pw_shell));
|
||||
S48_GC_UNPROTECT();
|
||||
return S48_TRUE;
|
||||
}
|
||||
|
||||
int group_info_gid (int gid, char **name, char ***members, int *nmembers)
|
||||
|
||||
|
||||
s48_value group_info_gid (s48_value scheme_gid, s48_value group_info_record)
|
||||
{
|
||||
struct group *grp = getgrgid(gid);
|
||||
if( !grp ) {
|
||||
name = 0;
|
||||
members = 0;
|
||||
nmembers = 0;
|
||||
return 0;
|
||||
}
|
||||
*name = grp->gr_name;
|
||||
*members = grp->gr_mem;
|
||||
*nmembers = strvec_len(grp->gr_mem);
|
||||
return 1;
|
||||
}
|
||||
struct group *grp = getgrgid(s48_extract_fixnum(scheme_gid));
|
||||
s48_value member_list;
|
||||
S48_DECLARE_GC_PROTECT(1);
|
||||
|
||||
S48_GC_PROTECT_1(group_info_record);
|
||||
|
||||
int group_info_name (const char *name,
|
||||
int *gid, char ***members, int *nmembers)
|
||||
{
|
||||
struct group *grp = getgrnam(name);
|
||||
if( !grp ) {
|
||||
gid = 0;
|
||||
members = 0;
|
||||
nmembers = 0;
|
||||
return 0;
|
||||
}
|
||||
*gid = grp->gr_gid;
|
||||
*members = grp->gr_mem;
|
||||
*nmembers = strvec_len(grp->gr_mem);
|
||||
return 1;
|
||||
S48_GC_UNPROTECT();
|
||||
return S48_FALSE;
|
||||
}
|
||||
S48_RECORD_SET(group_info_record, 0, s48_enter_string (grp->gr_name));
|
||||
|
||||
member_list = char_pp_2_string_list (grp->gr_mem);
|
||||
if( !member_list ) {
|
||||
S48_GC_UNPROTECT();
|
||||
return S48_FALSE;
|
||||
}
|
||||
S48_RECORD_SET(group_info_record, 2, member_list);
|
||||
|
||||
S48_GC_UNPROTECT();
|
||||
return S48_TRUE;
|
||||
}
|
||||
|
||||
s48_value group_info_name (s48_value scheme_name, s48_value group_info_record)
|
||||
{
|
||||
struct group *grp = getgrnam(s48_extract_string(scheme_name));
|
||||
s48_value member_list;
|
||||
S48_DECLARE_GC_PROTECT(1);
|
||||
|
||||
S48_GC_PROTECT_1(group_info_record);
|
||||
|
||||
if( !grp ) {
|
||||
S48_GC_UNPROTECT();
|
||||
return S48_FALSE;
|
||||
}
|
||||
S48_RECORD_SET(group_info_record, 1, s48_enter_fixnum (grp->gr_gid));
|
||||
|
||||
member_list = char_pp_2_string_list (grp->gr_mem);
|
||||
if( !member_list ) {
|
||||
S48_GC_UNPROTECT();
|
||||
return S48_FALSE;
|
||||
}
|
||||
S48_RECORD_SET(group_info_record, 2, member_list);
|
||||
|
||||
S48_GC_UNPROTECT();
|
||||
return S48_TRUE;
|
||||
}
|
||||
|
||||
|
||||
s48_value s48_init_userinfo(void){
|
||||
S48_EXPORT_FUNCTION(user_info_uid);
|
||||
S48_EXPORT_FUNCTION(user_info_name);
|
||||
S48_EXPORT_FUNCTION(my_username);
|
||||
S48_EXPORT_FUNCTION(group_info_gid);
|
||||
S48_EXPORT_FUNCTION(group_info_name);
|
||||
return S48_UNSPECIFIC;
|
||||
}
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
/* Exports from userinfo1.c. */
|
||||
#include "scheme48.h"
|
||||
s48_value my_username(void);
|
||||
|
||||
char *my_username(void);
|
||||
s48_value user_info_uid(s48_value scheme_uid, s48_value user_info_record);
|
||||
|
||||
int user_info_uid(uid_t uid,
|
||||
char **name, gid_t *gid, char **dir, char **shell);
|
||||
|
||||
int user_info_name(const char *name,
|
||||
uid_t *uid, gid_t *gid, char **dir, char **shell);
|
||||
s48_value user_info_name(s48_value scheme_name, s48_value user_info_record);
|
||||
|
||||
int group_info_gid (int gid, char **name, char ***members, int *nmembers);
|
||||
s48_value group_info_gid (s48_value gid, s48_value group_info_record);
|
||||
|
||||
int group_info_name (const char *name,
|
||||
int *gid, char ***members, int *nmembers);
|
||||
s48_value group_info_name (s48_value name, s48_value group_info_record);
|
||||
|
|
Loading…
Reference in New Issue