|
Lines 114-119
Link Here
|
| 114 |
* this breaks. |
114 |
* this breaks. |
| 115 |
*/ |
115 |
*/ |
| 116 |
#ifdef UNSUPPORTED_POSIX_THREADS_HACK |
116 |
#ifdef UNSUPPORTED_POSIX_THREADS_HACK |
|
|
117 |
#if defined(USE_UTASK_HACK) |
| 118 |
struct task; |
| 119 |
typedef struct task *sp_pthread_t; |
| 120 |
#else /* !USE_UTASK_HACK */ |
| 117 |
#include <pthread.h> |
121 |
#include <pthread.h> |
| 118 |
/* |
122 |
/* |
| 119 |
* Avoid namespace clash when *not* using pthreads for systems *with* |
123 |
* Avoid namespace clash when *not* using pthreads for systems *with* |
|
Lines 121-126
Link Here
|
| 121 |
* (e.g. Linux) |
125 |
* (e.g. Linux) |
| 122 |
*/ |
126 |
*/ |
| 123 |
typedef pthread_t sp_pthread_t; |
127 |
typedef pthread_t sp_pthread_t; |
|
|
128 |
#endif /* !USE_UTASK_HACK */ |
| 124 |
#else |
129 |
#else |
| 125 |
typedef pid_t sp_pthread_t; |
130 |
typedef pid_t sp_pthread_t; |
| 126 |
#endif |
131 |
#endif |
|
Lines 135-142
Link Here
|
| 135 |
static void sshpam_free_ctx(void *); |
140 |
static void sshpam_free_ctx(void *); |
| 136 |
static struct pam_ctxt *cleanup_ctxt; |
141 |
static struct pam_ctxt *cleanup_ctxt; |
| 137 |
|
142 |
|
| 138 |
#ifndef UNSUPPORTED_POSIX_THREADS_HACK |
143 |
#if defined(UNSUPPORTED_POSIX_THREADS_HACK) && defined(USE_UTASK_HACK) |
| 139 |
/* |
144 |
/* |
|
|
145 |
* Simulate threads using setjmp/longjmp and sigaltstack. |
| 146 |
* This works for exactly two cooperative tasks, which is all that |
| 147 |
* PAM authentication needs. |
| 148 |
*/ |
| 149 |
|
| 150 |
#include <setjmp.h> |
| 151 |
#ifdef HAVE_POLL_H |
| 152 |
#include <poll.h> |
| 153 |
#endif |
| 154 |
|
| 155 |
#define utask_jmp_buf sigjmp_buf |
| 156 |
#define utask_setjmp(b) sigsetjmp(b, 0) |
| 157 |
#define utask_longjmp(b,x) siglongjmp(b, x) |
| 158 |
|
| 159 |
/* A task structure; mainly a container for the jmpbuf to save task state */ |
| 160 |
struct task { |
| 161 |
const char *name; |
| 162 |
enum { UTASK_DEAD, UTASK_RUNNABLE, UTASK_EXITED } state; |
| 163 |
utask_jmp_buf jmpbuf; |
| 164 |
void *(*entry)(void *); |
| 165 |
void *entry_arg; |
| 166 |
void *return_val; |
| 167 |
char *stack; |
| 168 |
struct task *next, **nextp; |
| 169 |
}; |
| 170 |
static struct task main_task = { "main", UTASK_RUNNABLE }; |
| 171 |
static struct task other_task = { "other", UTASK_DEAD }; |
| 172 |
static struct task * volatile current_task = &main_task; |
| 173 |
static struct task *sleeping_tasks; |
| 174 |
|
| 175 |
/* Switches to a task. */ |
| 176 |
static void |
| 177 |
utask_switch(struct task *new_task) |
| 178 |
{ |
| 179 |
struct task *old_task; |
| 180 |
|
| 181 |
if (current_task != new_task) { |
| 182 |
old_task = current_task; |
| 183 |
current_task = new_task; |
| 184 |
if (utask_setjmp(old_task->jmpbuf) == 0) |
| 185 |
utask_longjmp(current_task->jmpbuf, 1); |
| 186 |
} |
| 187 |
} |
| 188 |
|
| 189 |
/* Yields to next runnable task. (cancellation point) */ |
| 190 |
static void |
| 191 |
pthread_yield() |
| 192 |
{ |
| 193 |
struct task *next_task; |
| 194 |
|
| 195 |
/* Determine which task to yield to. Easy since there are only 2 tasks */ |
| 196 |
next_task = current_task->next; |
| 197 |
next_task = (current_task == &main_task ? &other_task : &main_task); |
| 198 |
|
| 199 |
if (next_task->state == UTASK_RUNNABLE) |
| 200 |
utask_switch(next_task); |
| 201 |
} |
| 202 |
|
| 203 |
/* Terminates the current task. */ |
| 204 |
static void |
| 205 |
pthread_exit(void *arg) |
| 206 |
{ |
| 207 |
if (current_task == &main_task) |
| 208 |
fatal("pthread_exit: main task exited"); |
| 209 |
current_task->return_val = arg; |
| 210 |
current_task->state = UTASK_EXITED; |
| 211 |
pthread_yield(); |
| 212 |
} |
| 213 |
|
| 214 |
/* Signal handler called for the USR1 signal raised from pthread_create() */ |
| 215 |
static void |
| 216 |
sig_usr1(int ign) |
| 217 |
{ |
| 218 |
/* We're on a completely different stack now, so switch task pointers |
| 219 |
* to run the new task. */ |
| 220 |
current_task->state = UTASK_RUNNABLE; |
| 221 |
pthread_yield(); |
| 222 |
pthread_exit((*other_task.entry)(other_task.entry_arg)); |
| 223 |
} |
| 224 |
|
| 225 |
/* Creates a second task/thread that runs on a separate stack. */ |
| 226 |
static int |
| 227 |
pthread_create(struct task **t, void *attr, void *(*entry)(void *), void *arg) |
| 228 |
{ |
| 229 |
stack_t sigstk, oldsigstk; |
| 230 |
struct sigaction sigact, oldsigact; |
| 231 |
struct task *new_task; |
| 232 |
int stacksz, stackpad; |
| 233 |
|
| 234 |
if (current_task != &main_task) |
| 235 |
fatal("pthread_create: not called from main task"); |
| 236 |
if (other_task.state == UTASK_RUNNABLE) |
| 237 |
fatal("pthread_create: other task still active"); |
| 238 |
|
| 239 |
/* Choosing a new task structure is easy. */ |
| 240 |
new_task = &other_task; |
| 241 |
|
| 242 |
new_task->entry = entry; |
| 243 |
new_task->entry_arg = arg; |
| 244 |
|
| 245 |
/* 4MB of stack with 8kB of padding either side */ |
| 246 |
stacksz = 4 * 1024 * 1024; |
| 247 |
stackpad = 8 * 1024; |
| 248 |
if (!new_task->stack) |
| 249 |
new_task->stack = xmalloc(stacksz + stackpad * 2); /* 4MB */ |
| 250 |
|
| 251 |
/* Set up an alternate signal stack, which is the stack that |
| 252 |
* sig_usr1() will run on. We use this to separate the tasks stacks, |
| 253 |
* in a kind-of-portable way. */ |
| 254 |
memset(&sigstk, 0, sizeof sigstk); |
| 255 |
sigstk.ss_sp = new_task->stack + stackpad; |
| 256 |
sigstk.ss_size = stacksz; |
| 257 |
sigstk.ss_flags = 0; |
| 258 |
if (sigaltstack(&sigstk, &oldsigstk) < 0) |
| 259 |
err(1, "sigaltstack"); |
| 260 |
|
| 261 |
/* Set up SIGUSR1 to call sig_usr1() */ |
| 262 |
memset(&sigact, 0, sizeof sigact); |
| 263 |
sigact.sa_handler = sig_usr1; |
| 264 |
sigact.sa_flags = /* SA_RESETHAND | */ SA_ONSTACK; |
| 265 |
if (sigaction(SIGUSR1, &sigact, &oldsigact) < 0) |
| 266 |
err(1, "sigaction"); |
| 267 |
|
| 268 |
if (utask_setjmp(current_task->jmpbuf) == 0) { |
| 269 |
/* Trigger sig_usr1() */ |
| 270 |
current_task = new_task; |
| 271 |
if (kill(getpid(), SIGUSR1) == -1) |
| 272 |
err(1, "kill"); |
| 273 |
while (new_task->state != UTASK_RUNNABLE) |
| 274 |
pause(); |
| 275 |
/* The signal handler returned (usually from pthread_exit()). */ |
| 276 |
current_task = &main_task; |
| 277 |
utask_longjmp(current_task->jmpbuf, 1); |
| 278 |
} |
| 279 |
/* sig_usr1() will longjmp back to here via pthread_yield() */ |
| 280 |
|
| 281 |
/* Restore the signal stack */ |
| 282 |
if (sigaltstack(&oldsigstk, (stack_t *)0) < 0) |
| 283 |
err(1, "sigaltstack"); |
| 284 |
/* Restore the signal handler to its old behaviour */ |
| 285 |
if (sigaction(SIGUSR1, &oldsigact, (struct sigaction *)0) < 0) |
| 286 |
err(1, "sigaction"); |
| 287 |
|
| 288 |
/* Yield back to the newly started task */ |
| 289 |
pthread_yield(); |
| 290 |
|
| 291 |
if (t) |
| 292 |
*t = new_task; |
| 293 |
return 0; |
| 294 |
} |
| 295 |
|
| 296 |
/* Waits for a task to exit. (cancellation point) */ |
| 297 |
static int |
| 298 |
pthread_join(struct task *t, void **ret) |
| 299 |
{ |
| 300 |
pthread_yield(); |
| 301 |
if (current_task != &main_task) |
| 302 |
errx(1, "pthread_join: only callable from the main task"); |
| 303 |
while (t->state == UTASK_RUNNABLE) |
| 304 |
pthread_yield(); |
| 305 |
if (t->stack) { |
| 306 |
free(t->stack); |
| 307 |
t->stack = NULL; |
| 308 |
} |
| 309 |
if (ret) |
| 310 |
*ret = t->return_val; |
| 311 |
return 0; |
| 312 |
} |
| 313 |
|
| 314 |
/* Cancels a task. */ |
| 315 |
static int |
| 316 |
pthread_cancel(struct task *t) |
| 317 |
{ |
| 318 |
/* Because these tasks are cooperative, they must be at |
| 319 |
* a cancellation point */ |
| 320 |
if (t->state == UTASK_RUNNABLE) { |
| 321 |
t->state = UTASK_DEAD; |
| 322 |
t->return_val = 0; |
| 323 |
} |
| 324 |
return 0; |
| 325 |
} |
| 326 |
|
| 327 |
/* Yields until a file descriptor becomes ready for read (cancellation point) */ |
| 328 |
static int |
| 329 |
utask_yield_fd(int fd) |
| 330 |
{ |
| 331 |
struct pollfd pfd[1]; |
| 332 |
int n; |
| 333 |
|
| 334 |
for (;;) { |
| 335 |
pfd[0].fd = fd; |
| 336 |
pfd[0].events = POLLIN; |
| 337 |
pfd[0].revents = 0; |
| 338 |
n = poll(pfd, 1, 0); |
| 339 |
if (n < 0) { |
| 340 |
if (errno == EAGAIN) |
| 341 |
continue; |
| 342 |
err(1, "utask_yield_fd: poll"); |
| 343 |
} else if (n > 0) |
| 344 |
return 1; |
| 345 |
pthread_yield(); |
| 346 |
} |
| 347 |
} |
| 348 |
|
| 349 |
/* The only function below that needs to yield is ssh_msg_recv */ |
| 350 |
#define ssh_msg_recv(s,b) (utask_yield_fd(s), ssh_msg_recv(s,b)) |
| 351 |
|
| 352 |
#else |
| 353 |
/* |
| 140 |
* Simulate threads with processes. |
354 |
* Simulate threads with processes. |
| 141 |
*/ |
355 |
*/ |
| 142 |
|
356 |
|