1 1.1 christos /* 2 1.1.1.3 christos * Copyright 2015-2022 The OpenSSL Project Authors. All Rights Reserved. 3 1.1 christos * 4 1.1.1.3 christos * Licensed under the Apache License 2.0 (the "License"). You may not use 5 1.1 christos * this file except in compliance with the License. You can obtain a copy 6 1.1 christos * in the file LICENSE in the source distribution or at 7 1.1 christos * https://www.openssl.org/source/license.html 8 1.1 christos */ 9 1.1 christos 10 1.1 christos #ifdef _WIN32 11 1.1 christos # include <windows.h> 12 1.1 christos #endif 13 1.1 christos 14 1.1 christos #include <stdio.h> 15 1.1 christos #include <string.h> 16 1.1 christos #include <openssl/async.h> 17 1.1 christos #include <openssl/crypto.h> 18 1.1 christos 19 1.1 christos static int ctr = 0; 20 1.1 christos static ASYNC_JOB *currjob = NULL; 21 1.1 christos 22 1.1 christos static int only_pause(void *args) 23 1.1 christos { 24 1.1 christos ASYNC_pause_job(); 25 1.1 christos 26 1.1 christos return 1; 27 1.1 christos } 28 1.1 christos 29 1.1 christos static int add_two(void *args) 30 1.1 christos { 31 1.1 christos ctr++; 32 1.1 christos ASYNC_pause_job(); 33 1.1 christos ctr++; 34 1.1 christos 35 1.1 christos return 2; 36 1.1 christos } 37 1.1 christos 38 1.1 christos static int save_current(void *args) 39 1.1 christos { 40 1.1 christos currjob = ASYNC_get_current_job(); 41 1.1 christos ASYNC_pause_job(); 42 1.1 christos 43 1.1 christos return 1; 44 1.1 christos } 45 1.1 christos 46 1.1.1.3 christos static int change_deflt_libctx(void *args) 47 1.1.1.3 christos { 48 1.1.1.3 christos OSSL_LIB_CTX *libctx = OSSL_LIB_CTX_new(); 49 1.1.1.3 christos OSSL_LIB_CTX *oldctx, *tmpctx; 50 1.1.1.3 christos int ret = 0; 51 1.1.1.3 christos 52 1.1.1.3 christos if (libctx == NULL) 53 1.1.1.3 christos return 0; 54 1.1.1.3 christos 55 1.1.1.3 christos oldctx = OSSL_LIB_CTX_set0_default(libctx); 56 1.1.1.3 christos ASYNC_pause_job(); 57 1.1.1.3 christos 58 1.1.1.3 christos /* Check the libctx is set up as we expect */ 59 1.1.1.3 christos tmpctx = OSSL_LIB_CTX_set0_default(oldctx); 60 1.1.1.3 christos if (tmpctx != libctx) 61 1.1.1.3 christos goto err; 62 1.1.1.3 christos 63 1.1.1.3 christos /* Set it back again to continue to use our own libctx */ 64 1.1.1.3 christos oldctx = OSSL_LIB_CTX_set0_default(libctx); 65 1.1.1.3 christos ASYNC_pause_job(); 66 1.1.1.3 christos 67 1.1.1.3 christos /* Check the libctx is set up as we expect */ 68 1.1.1.3 christos tmpctx = OSSL_LIB_CTX_set0_default(oldctx); 69 1.1.1.3 christos if (tmpctx != libctx) 70 1.1.1.3 christos goto err; 71 1.1.1.3 christos 72 1.1.1.3 christos ret = 1; 73 1.1.1.3 christos err: 74 1.1.1.3 christos OSSL_LIB_CTX_free(libctx); 75 1.1.1.3 christos return ret; 76 1.1.1.3 christos } 77 1.1.1.3 christos 78 1.1.1.3 christos 79 1.1 christos #define MAGIC_WAIT_FD ((OSSL_ASYNC_FD)99) 80 1.1 christos static int waitfd(void *args) 81 1.1 christos { 82 1.1 christos ASYNC_JOB *job; 83 1.1 christos ASYNC_WAIT_CTX *waitctx; 84 1.1 christos job = ASYNC_get_current_job(); 85 1.1 christos if (job == NULL) 86 1.1 christos return 0; 87 1.1 christos waitctx = ASYNC_get_wait_ctx(job); 88 1.1 christos if (waitctx == NULL) 89 1.1 christos return 0; 90 1.1 christos 91 1.1 christos /* First case: no fd added or removed */ 92 1.1 christos ASYNC_pause_job(); 93 1.1 christos 94 1.1 christos /* Second case: one fd added */ 95 1.1 christos if (!ASYNC_WAIT_CTX_set_wait_fd(waitctx, waitctx, MAGIC_WAIT_FD, NULL, NULL)) 96 1.1 christos return 0; 97 1.1 christos ASYNC_pause_job(); 98 1.1 christos 99 1.1 christos /* Third case: all fd removed */ 100 1.1 christos if (!ASYNC_WAIT_CTX_clear_fd(waitctx, waitctx)) 101 1.1 christos return 0; 102 1.1 christos ASYNC_pause_job(); 103 1.1 christos 104 1.1 christos /* Last case: fd added and immediately removed */ 105 1.1 christos if (!ASYNC_WAIT_CTX_set_wait_fd(waitctx, waitctx, MAGIC_WAIT_FD, NULL, NULL)) 106 1.1 christos return 0; 107 1.1 christos if (!ASYNC_WAIT_CTX_clear_fd(waitctx, waitctx)) 108 1.1 christos return 0; 109 1.1 christos 110 1.1 christos return 1; 111 1.1 christos } 112 1.1 christos 113 1.1 christos static int blockpause(void *args) 114 1.1 christos { 115 1.1 christos ASYNC_block_pause(); 116 1.1 christos ASYNC_pause_job(); 117 1.1 christos ASYNC_unblock_pause(); 118 1.1 christos ASYNC_pause_job(); 119 1.1 christos 120 1.1 christos return 1; 121 1.1 christos } 122 1.1 christos 123 1.1.1.2 christos static int test_ASYNC_init_thread(void) 124 1.1 christos { 125 1.1 christos ASYNC_JOB *job1 = NULL, *job2 = NULL, *job3 = NULL; 126 1.1 christos int funcret1, funcret2, funcret3; 127 1.1 christos ASYNC_WAIT_CTX *waitctx = NULL; 128 1.1 christos 129 1.1 christos if ( !ASYNC_init_thread(2, 0) 130 1.1 christos || (waitctx = ASYNC_WAIT_CTX_new()) == NULL 131 1.1 christos || ASYNC_start_job(&job1, waitctx, &funcret1, only_pause, NULL, 0) 132 1.1 christos != ASYNC_PAUSE 133 1.1 christos || ASYNC_start_job(&job2, waitctx, &funcret2, only_pause, NULL, 0) 134 1.1 christos != ASYNC_PAUSE 135 1.1 christos || ASYNC_start_job(&job3, waitctx, &funcret3, only_pause, NULL, 0) 136 1.1 christos != ASYNC_NO_JOBS 137 1.1 christos || ASYNC_start_job(&job1, waitctx, &funcret1, only_pause, NULL, 0) 138 1.1 christos != ASYNC_FINISH 139 1.1 christos || ASYNC_start_job(&job3, waitctx, &funcret3, only_pause, NULL, 0) 140 1.1 christos != ASYNC_PAUSE 141 1.1 christos || ASYNC_start_job(&job2, waitctx, &funcret2, only_pause, NULL, 0) 142 1.1 christos != ASYNC_FINISH 143 1.1 christos || ASYNC_start_job(&job3, waitctx, &funcret3, only_pause, NULL, 0) 144 1.1 christos != ASYNC_FINISH 145 1.1 christos || funcret1 != 1 146 1.1 christos || funcret2 != 1 147 1.1 christos || funcret3 != 1) { 148 1.1 christos fprintf(stderr, "test_ASYNC_init_thread() failed\n"); 149 1.1 christos ASYNC_WAIT_CTX_free(waitctx); 150 1.1 christos ASYNC_cleanup_thread(); 151 1.1 christos return 0; 152 1.1 christos } 153 1.1 christos 154 1.1 christos ASYNC_WAIT_CTX_free(waitctx); 155 1.1 christos ASYNC_cleanup_thread(); 156 1.1 christos return 1; 157 1.1 christos } 158 1.1 christos 159 1.1.1.3 christos static int test_callback(void *arg) 160 1.1.1.3 christos { 161 1.1.1.3 christos printf("callback test pass\n"); 162 1.1.1.3 christos return 1; 163 1.1.1.3 christos } 164 1.1.1.3 christos 165 1.1.1.3 christos static int test_ASYNC_callback_status(void) 166 1.1.1.3 christos { 167 1.1.1.3 christos ASYNC_WAIT_CTX *waitctx = NULL; 168 1.1.1.3 christos int set_arg = 100; 169 1.1.1.3 christos ASYNC_callback_fn get_callback; 170 1.1.1.3 christos void *get_arg; 171 1.1.1.3 christos int set_status = 1; 172 1.1.1.3 christos 173 1.1.1.3 christos if ( !ASYNC_init_thread(1, 0) 174 1.1.1.3 christos || (waitctx = ASYNC_WAIT_CTX_new()) == NULL 175 1.1.1.3 christos || ASYNC_WAIT_CTX_set_callback(waitctx, test_callback, (void*)&set_arg) 176 1.1.1.3 christos != 1 177 1.1.1.3 christos || ASYNC_WAIT_CTX_get_callback(waitctx, &get_callback, &get_arg) 178 1.1.1.3 christos != 1 179 1.1.1.3 christos || test_callback != get_callback 180 1.1.1.3 christos || get_arg != (void*)&set_arg 181 1.1.1.3 christos || (*get_callback)(get_arg) != 1 182 1.1.1.3 christos || ASYNC_WAIT_CTX_set_status(waitctx, set_status) != 1 183 1.1.1.3 christos || set_status != ASYNC_WAIT_CTX_get_status(waitctx)) { 184 1.1.1.3 christos fprintf(stderr, "test_ASYNC_callback_status() failed\n"); 185 1.1.1.3 christos ASYNC_WAIT_CTX_free(waitctx); 186 1.1.1.3 christos ASYNC_cleanup_thread(); 187 1.1.1.3 christos return 0; 188 1.1.1.3 christos } 189 1.1.1.3 christos 190 1.1.1.3 christos ASYNC_WAIT_CTX_free(waitctx); 191 1.1.1.3 christos ASYNC_cleanup_thread(); 192 1.1.1.3 christos return 1; 193 1.1.1.3 christos 194 1.1.1.3 christos } 195 1.1.1.3 christos 196 1.1.1.2 christos static int test_ASYNC_start_job(void) 197 1.1 christos { 198 1.1 christos ASYNC_JOB *job = NULL; 199 1.1 christos int funcret; 200 1.1 christos ASYNC_WAIT_CTX *waitctx = NULL; 201 1.1 christos 202 1.1 christos ctr = 0; 203 1.1 christos 204 1.1 christos if ( !ASYNC_init_thread(1, 0) 205 1.1 christos || (waitctx = ASYNC_WAIT_CTX_new()) == NULL 206 1.1 christos || ASYNC_start_job(&job, waitctx, &funcret, add_two, NULL, 0) 207 1.1 christos != ASYNC_PAUSE 208 1.1 christos || ctr != 1 209 1.1 christos || ASYNC_start_job(&job, waitctx, &funcret, add_two, NULL, 0) 210 1.1 christos != ASYNC_FINISH 211 1.1 christos || ctr != 2 212 1.1 christos || funcret != 2) { 213 1.1 christos fprintf(stderr, "test_ASYNC_start_job() failed\n"); 214 1.1 christos ASYNC_WAIT_CTX_free(waitctx); 215 1.1 christos ASYNC_cleanup_thread(); 216 1.1 christos return 0; 217 1.1 christos } 218 1.1 christos 219 1.1 christos ASYNC_WAIT_CTX_free(waitctx); 220 1.1 christos ASYNC_cleanup_thread(); 221 1.1 christos return 1; 222 1.1 christos } 223 1.1 christos 224 1.1.1.2 christos static int test_ASYNC_get_current_job(void) 225 1.1 christos { 226 1.1 christos ASYNC_JOB *job = NULL; 227 1.1 christos int funcret; 228 1.1 christos ASYNC_WAIT_CTX *waitctx = NULL; 229 1.1 christos 230 1.1 christos currjob = NULL; 231 1.1 christos 232 1.1 christos if ( !ASYNC_init_thread(1, 0) 233 1.1 christos || (waitctx = ASYNC_WAIT_CTX_new()) == NULL 234 1.1 christos || ASYNC_start_job(&job, waitctx, &funcret, save_current, NULL, 0) 235 1.1 christos != ASYNC_PAUSE 236 1.1 christos || currjob != job 237 1.1 christos || ASYNC_start_job(&job, waitctx, &funcret, save_current, NULL, 0) 238 1.1 christos != ASYNC_FINISH 239 1.1 christos || funcret != 1) { 240 1.1 christos fprintf(stderr, "test_ASYNC_get_current_job() failed\n"); 241 1.1 christos ASYNC_WAIT_CTX_free(waitctx); 242 1.1 christos ASYNC_cleanup_thread(); 243 1.1 christos return 0; 244 1.1 christos } 245 1.1 christos 246 1.1 christos ASYNC_WAIT_CTX_free(waitctx); 247 1.1 christos ASYNC_cleanup_thread(); 248 1.1 christos return 1; 249 1.1 christos } 250 1.1 christos 251 1.1.1.2 christos static int test_ASYNC_WAIT_CTX_get_all_fds(void) 252 1.1 christos { 253 1.1 christos ASYNC_JOB *job = NULL; 254 1.1 christos int funcret; 255 1.1 christos ASYNC_WAIT_CTX *waitctx = NULL; 256 1.1 christos OSSL_ASYNC_FD fd = OSSL_BAD_ASYNC_FD, delfd = OSSL_BAD_ASYNC_FD; 257 1.1 christos size_t numfds, numdelfds; 258 1.1 christos 259 1.1 christos if ( !ASYNC_init_thread(1, 0) 260 1.1 christos || (waitctx = ASYNC_WAIT_CTX_new()) == NULL 261 1.1 christos /* On first run we're not expecting any wait fds */ 262 1.1 christos || ASYNC_start_job(&job, waitctx, &funcret, waitfd, NULL, 0) 263 1.1 christos != ASYNC_PAUSE 264 1.1 christos || !ASYNC_WAIT_CTX_get_all_fds(waitctx, NULL, &numfds) 265 1.1 christos || numfds != 0 266 1.1 christos || !ASYNC_WAIT_CTX_get_changed_fds(waitctx, NULL, &numfds, NULL, 267 1.1 christos &numdelfds) 268 1.1 christos || numfds != 0 269 1.1 christos || numdelfds != 0 270 1.1 christos /* On second run we're expecting one added fd */ 271 1.1 christos || ASYNC_start_job(&job, waitctx, &funcret, waitfd, NULL, 0) 272 1.1 christos != ASYNC_PAUSE 273 1.1 christos || !ASYNC_WAIT_CTX_get_all_fds(waitctx, NULL, &numfds) 274 1.1 christos || numfds != 1 275 1.1 christos || !ASYNC_WAIT_CTX_get_all_fds(waitctx, &fd, &numfds) 276 1.1 christos || fd != MAGIC_WAIT_FD 277 1.1 christos || (fd = OSSL_BAD_ASYNC_FD, 0) /* Assign to something else */ 278 1.1 christos || !ASYNC_WAIT_CTX_get_changed_fds(waitctx, NULL, &numfds, NULL, 279 1.1 christos &numdelfds) 280 1.1 christos || numfds != 1 281 1.1 christos || numdelfds != 0 282 1.1 christos || !ASYNC_WAIT_CTX_get_changed_fds(waitctx, &fd, &numfds, NULL, 283 1.1 christos &numdelfds) 284 1.1 christos || fd != MAGIC_WAIT_FD 285 1.1 christos /* On third run we expect one deleted fd */ 286 1.1 christos || ASYNC_start_job(&job, waitctx, &funcret, waitfd, NULL, 0) 287 1.1 christos != ASYNC_PAUSE 288 1.1 christos || !ASYNC_WAIT_CTX_get_all_fds(waitctx, NULL, &numfds) 289 1.1 christos || numfds != 0 290 1.1 christos || !ASYNC_WAIT_CTX_get_changed_fds(waitctx, NULL, &numfds, NULL, 291 1.1 christos &numdelfds) 292 1.1 christos || numfds != 0 293 1.1 christos || numdelfds != 1 294 1.1 christos || !ASYNC_WAIT_CTX_get_changed_fds(waitctx, NULL, &numfds, &delfd, 295 1.1 christos &numdelfds) 296 1.1 christos || delfd != MAGIC_WAIT_FD 297 1.1 christos /* On last run we are not expecting any wait fd */ 298 1.1 christos || ASYNC_start_job(&job, waitctx, &funcret, waitfd, NULL, 0) 299 1.1 christos != ASYNC_FINISH 300 1.1 christos || !ASYNC_WAIT_CTX_get_all_fds(waitctx, NULL, &numfds) 301 1.1 christos || numfds != 0 302 1.1 christos || !ASYNC_WAIT_CTX_get_changed_fds(waitctx, NULL, &numfds, NULL, 303 1.1 christos &numdelfds) 304 1.1 christos || numfds != 0 305 1.1 christos || numdelfds != 0 306 1.1 christos || funcret != 1) { 307 1.1 christos fprintf(stderr, "test_ASYNC_get_wait_fd() failed\n"); 308 1.1 christos ASYNC_WAIT_CTX_free(waitctx); 309 1.1 christos ASYNC_cleanup_thread(); 310 1.1 christos return 0; 311 1.1 christos } 312 1.1 christos 313 1.1 christos ASYNC_WAIT_CTX_free(waitctx); 314 1.1 christos ASYNC_cleanup_thread(); 315 1.1 christos return 1; 316 1.1 christos } 317 1.1 christos 318 1.1.1.2 christos static int test_ASYNC_block_pause(void) 319 1.1 christos { 320 1.1 christos ASYNC_JOB *job = NULL; 321 1.1 christos int funcret; 322 1.1 christos ASYNC_WAIT_CTX *waitctx = NULL; 323 1.1 christos 324 1.1 christos if ( !ASYNC_init_thread(1, 0) 325 1.1 christos || (waitctx = ASYNC_WAIT_CTX_new()) == NULL 326 1.1 christos || ASYNC_start_job(&job, waitctx, &funcret, blockpause, NULL, 0) 327 1.1 christos != ASYNC_PAUSE 328 1.1 christos || ASYNC_start_job(&job, waitctx, &funcret, blockpause, NULL, 0) 329 1.1 christos != ASYNC_FINISH 330 1.1 christos || funcret != 1) { 331 1.1 christos fprintf(stderr, "test_ASYNC_block_pause() failed\n"); 332 1.1 christos ASYNC_WAIT_CTX_free(waitctx); 333 1.1 christos ASYNC_cleanup_thread(); 334 1.1 christos return 0; 335 1.1 christos } 336 1.1 christos 337 1.1 christos ASYNC_WAIT_CTX_free(waitctx); 338 1.1 christos ASYNC_cleanup_thread(); 339 1.1 christos return 1; 340 1.1 christos } 341 1.1 christos 342 1.1.1.3 christos static int test_ASYNC_start_job_ex(void) 343 1.1.1.3 christos { 344 1.1.1.3 christos ASYNC_JOB *job = NULL; 345 1.1.1.3 christos int funcret; 346 1.1.1.3 christos ASYNC_WAIT_CTX *waitctx = NULL; 347 1.1.1.3 christos OSSL_LIB_CTX *libctx = OSSL_LIB_CTX_new(); 348 1.1.1.3 christos OSSL_LIB_CTX *oldctx, *tmpctx, *globalctx; 349 1.1.1.3 christos int ret = 0; 350 1.1.1.3 christos 351 1.1.1.3 christos if (libctx == NULL) { 352 1.1.1.3 christos fprintf(stderr, 353 1.1.1.3 christos "test_ASYNC_start_job_ex() failed to create libctx\n"); 354 1.1.1.3 christos goto err; 355 1.1.1.3 christos } 356 1.1.1.3 christos 357 1.1.1.3 christos globalctx = oldctx = OSSL_LIB_CTX_set0_default(libctx); 358 1.1.1.3 christos 359 1.1.1.3 christos if ((waitctx = ASYNC_WAIT_CTX_new()) == NULL 360 1.1.1.3 christos || ASYNC_start_job(&job, waitctx, &funcret, change_deflt_libctx, 361 1.1.1.3 christos NULL, 0) 362 1.1.1.3 christos != ASYNC_PAUSE) { 363 1.1.1.3 christos fprintf(stderr, 364 1.1.1.3 christos "test_ASYNC_start_job_ex() failed to start job\n"); 365 1.1.1.3 christos goto err; 366 1.1.1.3 christos } 367 1.1.1.3 christos 368 1.1.1.3 christos /* Reset the libctx temporarily to find out what it is*/ 369 1.1.1.3 christos tmpctx = OSSL_LIB_CTX_set0_default(oldctx); 370 1.1.1.3 christos oldctx = OSSL_LIB_CTX_set0_default(tmpctx); 371 1.1.1.3 christos if (tmpctx != libctx) { 372 1.1.1.3 christos fprintf(stderr, 373 1.1.1.3 christos "test_ASYNC_start_job_ex() failed - unexpected libctx\n"); 374 1.1.1.3 christos goto err; 375 1.1.1.3 christos } 376 1.1.1.3 christos 377 1.1.1.3 christos if (ASYNC_start_job(&job, waitctx, &funcret, change_deflt_libctx, NULL, 0) 378 1.1.1.3 christos != ASYNC_PAUSE) { 379 1.1.1.3 christos fprintf(stderr, 380 1.1.1.3 christos "test_ASYNC_start_job_ex() - restarting job failed\n"); 381 1.1.1.3 christos goto err; 382 1.1.1.3 christos } 383 1.1.1.3 christos 384 1.1.1.3 christos /* Reset the libctx and continue with the global default libctx */ 385 1.1.1.3 christos tmpctx = OSSL_LIB_CTX_set0_default(oldctx); 386 1.1.1.3 christos if (tmpctx != libctx) { 387 1.1.1.3 christos fprintf(stderr, 388 1.1.1.3 christos "test_ASYNC_start_job_ex() failed - unexpected libctx\n"); 389 1.1.1.3 christos goto err; 390 1.1.1.3 christos } 391 1.1.1.3 christos 392 1.1.1.3 christos if (ASYNC_start_job(&job, waitctx, &funcret, change_deflt_libctx, NULL, 0) 393 1.1.1.3 christos != ASYNC_FINISH 394 1.1.1.3 christos || funcret != 1) { 395 1.1.1.3 christos fprintf(stderr, 396 1.1.1.3 christos "test_ASYNC_start_job_ex() - finishing job failed\n"); 397 1.1.1.3 christos goto err; 398 1.1.1.3 christos } 399 1.1.1.3 christos 400 1.1.1.3 christos /* Reset the libctx temporarily to find out what it is*/ 401 1.1.1.3 christos tmpctx = OSSL_LIB_CTX_set0_default(libctx); 402 1.1.1.3 christos OSSL_LIB_CTX_set0_default(tmpctx); 403 1.1.1.3 christos if (tmpctx != globalctx) { 404 1.1.1.3 christos fprintf(stderr, 405 1.1.1.3 christos "test_ASYNC_start_job_ex() failed - global libctx check failed\n"); 406 1.1.1.3 christos goto err; 407 1.1.1.3 christos } 408 1.1.1.3 christos 409 1.1.1.3 christos ret = 1; 410 1.1.1.3 christos err: 411 1.1.1.3 christos ASYNC_WAIT_CTX_free(waitctx); 412 1.1.1.3 christos ASYNC_cleanup_thread(); 413 1.1.1.3 christos OSSL_LIB_CTX_free(libctx); 414 1.1.1.3 christos return ret; 415 1.1.1.3 christos } 416 1.1.1.3 christos 417 1.1 christos int main(int argc, char **argv) 418 1.1 christos { 419 1.1 christos if (!ASYNC_is_capable()) { 420 1.1 christos fprintf(stderr, 421 1.1 christos "OpenSSL build is not ASYNC capable - skipping async tests\n"); 422 1.1 christos } else { 423 1.1.1.3 christos if (!test_ASYNC_init_thread() 424 1.1.1.3 christos || !test_ASYNC_callback_status() 425 1.1 christos || !test_ASYNC_start_job() 426 1.1 christos || !test_ASYNC_get_current_job() 427 1.1 christos || !test_ASYNC_WAIT_CTX_get_all_fds() 428 1.1.1.3 christos || !test_ASYNC_block_pause() 429 1.1.1.3 christos || !test_ASYNC_start_job_ex()) { 430 1.1 christos return 1; 431 1.1 christos } 432 1.1 christos } 433 1.1 christos printf("PASS\n"); 434 1.1 christos return 0; 435 1.1 christos } 436