static char *uwsgi_cgi_get_docroot(char *path_info, uint16_t path_info_len, int *need_free, int *is_a_file, int *discard_base, char **script_name) { struct uwsgi_dyn_dict *udd = uc.mountpoint, *choosen_udd = NULL; int best_found = 0; struct stat st; char *path = NULL; if (uc.has_mountpoints) { while(udd) { if (udd->vallen) { if (!uwsgi_starts_with(path_info, path_info_len, udd->key, udd->keylen) && udd->keylen > best_found) { best_found = udd->keylen ; choosen_udd = udd; path = udd->value; *script_name = udd->key; *discard_base = udd->keylen; if (udd->key[udd->keylen-1] == '/') { *discard_base = *discard_base-1; } } } udd = udd->next; } } if (choosen_udd == NULL) { choosen_udd = uc.default_cgi; if (!choosen_udd) return NULL; path = choosen_udd->key; } if (choosen_udd->status == 0) { char *tmp_udd = uwsgi_malloc(PATH_MAX+1); if (!realpath(path, tmp_udd)) { free(tmp_udd); return NULL; } if (stat(tmp_udd, &st)) { uwsgi_error("stat()"); free(tmp_udd); return NULL; } if (!S_ISDIR(st.st_mode)) { *is_a_file = 1; } *need_free = 1; return tmp_udd; } if (choosen_udd->status == 2) *is_a_file = 1; return path; }
int uwsgi_static_want_gzip(struct wsgi_request *wsgi_req, char *filename, size_t filename_len, struct stat *st) { // check for filename size if (filename_len + 4 > PATH_MAX) return 0; // check for supported encodings if (!uwsgi_contains_n(wsgi_req->encoding, wsgi_req->encoding_len, "gzip", 4) ) return 0; // check for 'all' if (uwsgi.static_gzip_all) goto gzip; // check for dirs/prefix struct uwsgi_string_list *usl = uwsgi.static_gzip_dir; while(usl) { if (!uwsgi_starts_with(filename, filename_len, usl->value, usl->len)) { goto gzip; } usl = usl->next; } // check for ext/suffix usl = uwsgi.static_gzip_ext; while(usl) { if (!uwsgi_strncmp(filename + (filename_len - usl->len), usl->len, usl->value, usl->len)) { goto gzip; } usl = usl->next; } #ifdef UWSGI_PCRE // check for regexp struct uwsgi_regexp_list *url = uwsgi.static_gzip; while(url) { if (uwsgi_regexp_match(url->pattern, url->pattern_extra, filename, filename_len) >= 0) { goto gzip; } url = url->next; } #endif return 0; gzip: memcpy(filename + filename_len, ".gz\0", 4); filename_len += 3; if (stat(filename, st)) { filename_len -= 3; filename[filename_len] = 0; return 0; } return 1; }
struct uwsgi_imperial_monitor *imperial_monitor_get_by_scheme(char *arg) { struct uwsgi_imperial_monitor *uim = uwsgi.emperor_monitors; while (uim) { char *scheme = uwsgi_concat2(uim->scheme, "://"); if (!uwsgi_starts_with(arg, strlen(arg), scheme, strlen(scheme))) { free(scheme); return uim; } free(scheme); uim = uim->next; } return NULL; }
static int uwsgi_webdav_add_props(struct wsgi_request *wsgi_req, xmlNode *req_prop, xmlNode * multistatus, xmlNsPtr dav_ns, char *uri, char *filename, int with_values) { struct stat st; if (stat(filename, &st)) { uwsgi_error("uwsgi_webdav_add_props()/stat()"); return -1; } int is_collection = 0; xmlNode *response = xmlNewChild(multistatus, dav_ns, BAD_CAST "response", NULL); uint16_t uri_len = strlen(uri) ; char *encoded_uri = uwsgi_malloc( (uri_len * 3) + 1); http_url_encode(uri, &uri_len, encoded_uri); encoded_uri[uri_len] = 0; xmlNewChild(response, dav_ns, BAD_CAST "href", BAD_CAST encoded_uri); free(encoded_uri); xmlNode *r_propstat = xmlNewChild(response, dav_ns, BAD_CAST "propstat", NULL); char *r_status = uwsgi_concat2n(wsgi_req->protocol, wsgi_req->protocol_len, " 200 OK", 7); xmlNewChild(r_propstat, dav_ns, BAD_CAST "status", BAD_CAST r_status); free(r_status); xmlNode *r_prop = xmlNewChild(r_propstat, dav_ns, BAD_CAST "prop", NULL); if (with_values) { if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "displayname")) { char *base_uri = uwsgi_get_last_char(uri, '/'); if (base_uri) { xmlNewChild(r_prop, dav_ns, BAD_CAST "displayname", BAD_CAST base_uri+1); } else { xmlNewChild(r_prop, dav_ns, BAD_CAST "displayname", BAD_CAST uri); } } if (S_ISDIR(st.st_mode)) is_collection = 1; xmlNode *r_type = NULL; if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "resourcetype")) { r_type = xmlNewChild(r_prop, dav_ns, BAD_CAST "resourcetype", NULL); if (is_collection) { xmlNewChild(r_type, dav_ns, BAD_CAST "collection", NULL); is_collection = 1; } } if (!is_collection) { if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "getcontentlength")) { char *r_contentlength = uwsgi_num2str(st.st_size); xmlNewChild(r_prop, dav_ns, BAD_CAST "getcontentlength", BAD_CAST r_contentlength); free(r_contentlength); } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "getcontenttype")) { size_t mime_type_len = 0; char *mime_type = uwsgi_get_mime_type(filename, strlen(filename), &mime_type_len); if (mime_type) { char *r_ctype = uwsgi_concat2n(mime_type, mime_type_len, "", 0); xmlNewTextChild(r_prop, dav_ns, BAD_CAST "getcontenttype", BAD_CAST r_ctype); free(r_ctype); } } } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "creationdate")) { // there is no creation date on UNIX/POSIX, ctime is the nearest thing... char *cdate = uwsgi_webdav_new_date(st.st_ctime); if (cdate) { xmlNewTextChild(r_prop, dav_ns, BAD_CAST "creationdate", BAD_CAST cdate); free(cdate); } } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "getlastmodified")) { char *mdate = uwsgi_webdav_new_date(st.st_mtime); if (mdate) { xmlNewTextChild(r_prop, dav_ns, BAD_CAST "getlastmodified", BAD_CAST mdate); free(mdate); } } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "getetag")) { char *etag = uwsgi_num2str(st.st_mtime); xmlNewTextChild(r_prop, dav_ns, BAD_CAST "getetag", BAD_CAST etag); free(etag); } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "executable")) { xmlNewChild(r_prop, dav_ns, BAD_CAST "executable", NULL); } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "owner")) { xmlNewTextChild(r_prop, dav_ns, BAD_CAST "owner", NULL); } if (wsgi_req->remote_user_len > 0) { if (udav.principal_base) { if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "current-user-principal")) { char *current_user_principal = uwsgi_concat2n(udav.principal_base, strlen(udav.principal_base), wsgi_req->remote_user, wsgi_req->remote_user_len); xmlNode *cup = xmlNewChild(r_prop, dav_ns, BAD_CAST "current-user-principal", NULL); xmlNewTextChild(cup, dav_ns, BAD_CAST "href", BAD_CAST current_user_principal); if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "resourcetype")) { if (!strcmp(current_user_principal, uri)) { xmlNewChild(r_type, dav_ns, BAD_CAST "principal", NULL); } } free(current_user_principal); } } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "current-user-privilege-set")) { xmlNode *cups = xmlNewChild(r_prop, dav_ns, BAD_CAST "current-user-privilege-set", NULL); xmlNode *privilege = xmlNewChild(cups, dav_ns, BAD_CAST "privilege", NULL); xmlNewChild(privilege, dav_ns, BAD_CAST "all", NULL); xmlNewChild(privilege, dav_ns, BAD_CAST "read", NULL); xmlNewChild(privilege, dav_ns, BAD_CAST "write", NULL); xmlNewChild(privilege, dav_ns, BAD_CAST "write-content", NULL); xmlNewChild(privilege, dav_ns, BAD_CAST "write-properties", NULL); } } if (uwsgi_webdav_prop_requested(req_prop, "DAV:", "supported-report-set")) { xmlNode *report_set = xmlNewChild(r_prop, dav_ns, BAD_CAST "supported-report-set", NULL); xmlNode *supported_report = xmlNewChild(report_set, dav_ns, BAD_CAST "supported-report", NULL); xmlNewChild(supported_report, dav_ns, BAD_CAST "report", BAD_CAST "principal-property-search"); supported_report = xmlNewChild(report_set, dav_ns, BAD_CAST "supported-report", NULL); xmlNewChild(supported_report, dav_ns, BAD_CAST "report", BAD_CAST "sync-collection"); supported_report = xmlNewChild(report_set, dav_ns, BAD_CAST "supported-report", NULL); xmlNewChild(supported_report, dav_ns, BAD_CAST "report", BAD_CAST "expand-property"); supported_report = xmlNewChild(report_set, dav_ns, BAD_CAST "supported-report", NULL); xmlNewChild(supported_report, dav_ns, BAD_CAST "report", BAD_CAST "principal-search-property-set"); } uwsgi_webdav_foreach_prop(udav.add_prop, req_prop, r_prop, 0, NULL ); uwsgi_webdav_foreach_prop(udav.add_prop_href, req_prop, r_prop, 1, NULL); uwsgi_webdav_foreach_prop(udav.add_prop_comp,req_prop, r_prop, 2 , NULL); uwsgi_webdav_foreach_prop(udav.add_rtype_prop,req_prop, r_type, 0, "resourcetype"); if (is_collection) { uwsgi_webdav_foreach_prop(udav.add_rtype_collection_prop,req_prop, r_type, 0, "resourcetype"); uwsgi_webdav_foreach_prop(udav.add_collection_prop,req_prop, r_prop, 0, NULL); uwsgi_webdav_foreach_prop(udav.add_collection_prop_href,req_prop, r_prop, 1, NULL); uwsgi_webdav_foreach_prop(udav.add_collection_prop_comp,req_prop, r_prop, 2, NULL); } else { uwsgi_webdav_foreach_prop(udav.add_rtype_object_prop,req_prop, r_type, 0, "resourcetype"); uwsgi_webdav_foreach_prop(udav.add_object_prop,req_prop, r_prop, 0, NULL); uwsgi_webdav_foreach_prop(udav.add_object_prop_href,req_prop, r_prop, 1, NULL); uwsgi_webdav_foreach_prop(udav.add_object_prop_comp,req_prop, r_prop, 2, NULL); } } else { xmlNewChild(r_prop, dav_ns, BAD_CAST "displayname", NULL); xmlNewChild(r_prop, dav_ns, BAD_CAST "resourcetype", NULL); if (!S_ISDIR(st.st_mode)) { xmlNewChild(r_prop, dav_ns, BAD_CAST "getcontentlength", NULL); xmlNewChild(r_prop, dav_ns, BAD_CAST "getcontenttype", NULL); } xmlNewChild(r_prop, dav_ns, BAD_CAST "creationdate", NULL); xmlNewChild(r_prop, dav_ns, BAD_CAST "getlastmodified", NULL); xmlNewChild(r_prop, dav_ns, BAD_CAST "supported-report-set", NULL); if (wsgi_req->remote_user_len > 0) { xmlNewChild(r_prop, dav_ns, BAD_CAST "current-user-privilege-set", NULL); if (udav.principal_base) { xmlNewChild(r_prop, dav_ns, BAD_CAST "current-user-principal", NULL); } } } #if defined(__linux__) || defined(__APPLE__) // get xattr for user.uwsgi.webdav. #if defined(__linux__) ssize_t rlen = listxattr(filename, NULL, 0); #elif defined(__APPLE__) ssize_t rlen = listxattr(filename, NULL, 0, 0); #endif // do not return -1 as the previous xml is valid !!! if (rlen <= 0) return 0; // use calloc to avoid races char *xattrs = uwsgi_calloc(rlen); #if defined(__linux__) if (listxattr(filename, xattrs, rlen) <= 0) { #elif defined(__APPLE__) if (listxattr(filename, xattrs, rlen, 0) <= 0) { #endif free(xattrs); return 0; } // parse the name list ssize_t i; char *key = NULL; for(i=0;i<rlen;i++) { // check for wrong condition if (xattrs[i] == 0 && key == NULL) break; if (key && xattrs[i] == 0) { if (!uwsgi_starts_with(key, strlen(key), "user.uwsgi.webdav.", 18)) { if (uwsgi_string_list_has_item(udav.skip_prop, key + 18, strlen(key + 18))) continue; xmlNsPtr xattr_ns = NULL; // does it has a namespace ? char *separator = strchr(key + 18, '|'); char *xattr_key = key + 18; if (separator) { xattr_key = separator + 1; *separator = 0; if (!uwsgi_webdav_prop_requested(req_prop, key + 18, xattr_key)) continue; } else { if (!uwsgi_webdav_prop_requested(req_prop, NULL, xattr_key)) continue; } xmlNode *xattr_item = NULL; if (with_values) { #if defined(__linux__) ssize_t rlen2 = getxattr(filename, key, NULL, 0); #elif defined(__APPLE__) ssize_t rlen2 = getxattr(filename, key, NULL, 0, 0, 0); #endif if (rlen > 0) { // leave space for final 0 char *xvalue = uwsgi_calloc(rlen2 + 1); #if defined(__linux__) if (getxattr(filename, key, xvalue, rlen2) > 0) { #elif defined(__APPLE__) if (getxattr(filename, key, xvalue, rlen2, 0 ,0) > 0) { #endif xattr_item = xmlNewTextChild(r_prop, NULL, BAD_CAST xattr_key, BAD_CAST xvalue); } free(xvalue); } else if (rlen == 0) { xattr_item = xmlNewTextChild(r_prop, NULL, BAD_CAST xattr_key, NULL); } } else { xattr_item = xmlNewTextChild(r_prop, NULL, BAD_CAST xattr_key, NULL); } if (separator && xattr_item) { xattr_ns = xmlNewNs(xattr_item, BAD_CAST (key + 18), NULL); *separator = '|'; xmlSetNs(xattr_item, xattr_ns); } } key = NULL; } else if (key == NULL) { key = &xattrs[i]; } } free(xattrs); #endif return 0; } static size_t uwsgi_webdav_expand_path(struct wsgi_request *wsgi_req, char *item, uint16_t item_len, char *filename) { struct uwsgi_app *ua = &uwsgi_apps[wsgi_req->app_id]; char *docroot = ua->interpreter; size_t docroot_len = strlen(docroot); // merge docroot with path_info char *tmp_filename = uwsgi_concat3n(docroot, docroot_len, "/", 1, item, item_len); // try expanding the path if (!realpath(tmp_filename, filename)) { free(tmp_filename); return 0; } free(tmp_filename); return strlen(filename); } static size_t uwsgi_webdav_expand_fake_path(struct wsgi_request *wsgi_req, char *item, uint16_t item_len, char *filename) { char *last_slash = uwsgi_get_last_charn(item, item_len, '/'); if (!last_slash) return 0; size_t filename_len = uwsgi_webdav_expand_path(wsgi_req, item, last_slash - item, filename); if (!filename_len) return 0; // check for overflow if (filename_len + (item_len - (last_slash - item)) >= PATH_MAX) return 0; memcpy(filename + filename_len, last_slash, (item_len - (last_slash - item))); filename_len += (item_len - (last_slash - item)); filename[(int)filename_len] = 0; return filename_len; } static xmlDoc *uwsgi_webdav_manage_prop(struct wsgi_request *wsgi_req, xmlNode *req_prop, char *filename, size_t filename_len, int with_values) { // default 1 depth int depth = 1; uint16_t http_depth_len = 0; char *http_depth = uwsgi_get_var(wsgi_req, "HTTP_DEPTH", 10, &http_depth_len); if (http_depth) { depth = uwsgi_str_num(http_depth, http_depth_len); } xmlDoc *rdoc = xmlNewDoc(BAD_CAST "1.0"); xmlNode *multistatus = xmlNewNode(NULL, BAD_CAST "multistatus"); xmlDocSetRootElement(rdoc, multistatus); xmlNsPtr dav_ns = xmlNewNs(multistatus, BAD_CAST "DAV:", BAD_CAST "D"); xmlSetNs(multistatus, dav_ns); if (depth == 0) { char *uri = uwsgi_concat2n(wsgi_req->path_info, wsgi_req->path_info_len, "", 0); uwsgi_webdav_add_props(wsgi_req, req_prop, multistatus, dav_ns, uri, filename, with_values); free(uri); } else { DIR *collection = opendir(filename); struct dirent de; for (;;) { struct dirent *de_r = NULL; if (readdir_r(collection, &de, &de_r)) { uwsgi_error("uwsgi_wevdav_manage_propfind()/readdir_r()"); break; } if (de_r == NULL) { break; } char *uri = NULL; char *direntry = NULL; if (!strcmp(de.d_name, "..")) { // skip .. continue; } else if (!strcmp(de.d_name, ".")) { uri = uwsgi_concat2n(wsgi_req->path_info, wsgi_req->path_info_len, "", 0); direntry = uwsgi_concat2n(filename, filename_len, "", 0); } else if (wsgi_req->path_info[wsgi_req->path_info_len - 1] == '/') { uri = uwsgi_concat2n(wsgi_req->path_info, wsgi_req->path_info_len, de.d_name, strlen(de.d_name)); direntry = uwsgi_concat3n(filename, filename_len, "/", 1, de.d_name, strlen(de.d_name)); } else { uri = uwsgi_concat3n(wsgi_req->path_info, wsgi_req->path_info_len, "/", 1, de.d_name, strlen(de.d_name)); direntry = uwsgi_concat3n(filename, filename_len, "/", 1, de.d_name, strlen(de.d_name)); } uwsgi_webdav_add_props(wsgi_req, req_prop, multistatus, dav_ns, uri, direntry, with_values); free(uri); free(direntry); } closedir(collection); } return rdoc; }
// "next" || "continue" || "break(.*)" || "goon" || "goto .+" static int uwsgi_routing_func_rpc_ret(struct wsgi_request *wsgi_req, struct uwsgi_route *ur) { int ret = -1; // this is the list of args char *argv[UMAX8]; // this is the size of each argument uint16_t argvs[UMAX8]; // this is a placeholder for tmp uwsgi_buffers struct uwsgi_buffer *ubs[UMAX8]; char **r_argv = (char **) ur->data2; uint16_t *r_argvs = (uint16_t *) ur->data3; char **subject = (char **) (((char *)(wsgi_req))+ur->subject); uint16_t *subject_len = (uint16_t *) (((char *)(wsgi_req))+ur->subject_len); uint64_t i; for(i=0;i<ur->custom;i++) { ubs[i] = uwsgi_routing_translate(wsgi_req, ur, *subject, *subject_len, r_argv[i], r_argvs[i]); if (!ubs[i]) goto end; argv[i] = ubs[i]->buf; argvs[i] = ubs[i]->pos; } // ok we now need to check it it is a local call or a remote one char *func = uwsgi_str(ur->data); char *remote = NULL; char *at = strchr(func, '@'); if (at) { *at = 0; remote = at+1; } uint16_t size; char *response = uwsgi_do_rpc(remote, func, ur->custom, argv, argvs, &size); free(func); if (!response) goto end; ret = UWSGI_ROUTE_CONTINUE; if (!uwsgi_strncmp(response, size, "next", 4 )) { ret = UWSGI_ROUTE_NEXT; } else if (!uwsgi_strncmp(response, size, "continue", 8 )) { ret = UWSGI_ROUTE_CONTINUE; } else if (!uwsgi_starts_with(response, size, "break", 5 )) { ret = UWSGI_ROUTE_BREAK; if (size > 6) { if (uwsgi_response_prepare_headers(wsgi_req, response+6, size-6)) goto end0; if (uwsgi_response_add_connection_close(wsgi_req)) goto end0; if (uwsgi_response_add_content_type(wsgi_req, "text/plain", 10)) goto end0; // no need to check for return value uwsgi_response_write_headers_do(wsgi_req); } } else if (!uwsgi_starts_with(response, size, "goto ", 5)) { ret = UWSGI_ROUTE_BREAK; if (size > 5) { // find the label struct uwsgi_route *routes = uwsgi.routes; while(routes) { if (!routes->label) goto next; if (!uwsgi_strncmp(routes->label, routes->label_len, response+5, size-5)) { ret = UWSGI_ROUTE_NEXT; wsgi_req->route_goto = routes->pos; goto found; } next: routes = routes->next; } goto end0; found: if (wsgi_req->route_goto <= wsgi_req->route_pc) { wsgi_req->route_goto = 0; uwsgi_log("[uwsgi-route] ERROR \"goto\" instruction can only jump forward (check your label !!!)\n"); ret = UWSGI_ROUTE_BREAK; } } } end0: free(response); end: for(i=0;i<ur->custom;i++) { if (ubs[i] != NULL) { uwsgi_buffer_destroy(ubs[i]); } } return ret; }
static uint16_t htpasswd_check(char *filename, char *auth) { char line[1024]; char *colon = strchr(auth, ':'); if (!colon) return 0; FILE *htpasswd = fopen(filename, "r"); if (!htpasswd) { return 0; } while(fgets(line, 1024, htpasswd)) { char *crypted = NULL; int need_free = 0; char *colon2 = strchr(line, ':'); if (!colon2) break; char *cpwd = colon2+1; size_t clen = strlen(cpwd); // now we check which algo to use // {SHA} ? if (!uwsgi_starts_with(cpwd, clen, "{SHA}", 5)) { crypted = htpasswd_check_sha1(colon+1); if (crypted) need_free = 1; goto check; } if (clen < 13) break; if (clen > 13) cpwd[13] = 0; #if defined(__linux__) && defined(__GLIBC__) struct crypt_data cd; memset(&cd, 0, sizeof(struct crypt_data)); /* work around glibc-2.2.5 bug, * has been fixed at some time in glibc-2.3.X */ #if (__GLIBC__ == 2) && \ (defined(__GLIBC_MINOR__) && __GLIBC_MINOR__ >= 2 && __GLIBC_MINOR__ < 4) // we do as nginx here cd.current_salt[0] = ~cpwd[0]; #endif crypted = crypt_r( colon+1, cpwd, &cd); #else if (uwsgi.threads > 1) pthread_mutex_lock(&ur_basicauth_crypt_mutex); crypted = crypt( colon+1, cpwd); if (uwsgi.threads > 1) pthread_mutex_unlock(&ur_basicauth_crypt_mutex); #endif check: if (!crypted) continue; if (!strcmp( crypted, cpwd )) { if (!uwsgi_strncmp(auth, colon-auth, line, colon2-line)) { fclose(htpasswd); if (need_free) free(crypted); return colon-auth; } } if (need_free) free(crypted); } fclose(htpasswd); return 0; }
int uwsgi_file_serve(struct wsgi_request *wsgi_req, char *document_root, uint16_t document_root_len, char *path_info, uint16_t path_info_len, int is_a_file) { struct stat st; char real_filename[PATH_MAX + 1]; size_t real_filename_len = 0; char *filename = NULL; size_t filename_len = 0; struct uwsgi_string_list *index = NULL; if (!is_a_file) { filename = uwsgi_concat3n(document_root, document_root_len, "/", 1, path_info, path_info_len); filename_len = document_root_len + 1 + path_info_len; } else { filename = uwsgi_concat2n(document_root, document_root_len, "", 0); filename_len = document_root_len; } #ifdef UWSGI_DEBUG uwsgi_log("[uwsgi-fileserve] checking for %s\n", filename); #endif if (uwsgi.static_cache_paths) { uwsgi_rlock(uwsgi.static_cache_paths->lock); uint64_t item_len; char *item = uwsgi_cache_get2(uwsgi.static_cache_paths, filename, filename_len, &item_len); if (item && item_len > 0 && item_len <= PATH_MAX) { memcpy(real_filename, item, item_len); real_filename_len = item_len; uwsgi_rwunlock(uwsgi.static_cache_paths->lock); goto found; } uwsgi_rwunlock(uwsgi.static_cache_paths->lock); } if (!realpath(filename, real_filename)) { #ifdef UWSGI_DEBUG uwsgi_log("[uwsgi-fileserve] unable to get realpath() of the static file\n"); #endif free(filename); return -1; } real_filename_len = strlen(real_filename); if (uwsgi.static_cache_paths) { uwsgi_wlock(uwsgi.static_cache_paths->lock); uwsgi_cache_set2(uwsgi.static_cache_paths, filename, filename_len, real_filename, real_filename_len, uwsgi.use_static_cache_paths, UWSGI_CACHE_FLAG_UPDATE); uwsgi_rwunlock(uwsgi.static_cache_paths->lock); } found: free(filename); if (uwsgi_starts_with(real_filename, real_filename_len, document_root, document_root_len)) { struct uwsgi_string_list *safe = uwsgi.static_safe; while(safe) { if (!uwsgi_starts_with(real_filename, real_filename_len, safe->value, safe->len)) { goto safe; } safe = safe->next; } uwsgi_log("[uwsgi-fileserve] security error: %s is not under %.*s or a safe path\n", real_filename, document_root_len, document_root); return -1; } safe: if (!uwsgi_static_stat(wsgi_req, real_filename, &real_filename_len, &st, &index)) { if (index) { // if we are here the PATH_INFO need to be changed if (uwsgi_req_append_path_info_with_index(wsgi_req, index->value, index->len)) { return -1; } } // skip methods other than GET and HEAD if (uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "GET", 3) && uwsgi_strncmp(wsgi_req->method, wsgi_req->method_len, "HEAD", 4)) { return -1; } // check for skippable ext struct uwsgi_string_list *sse = uwsgi.static_skip_ext; while (sse) { if (real_filename_len >= sse->len) { if (!uwsgi_strncmp(real_filename + (real_filename_len - sse->len), sse->len, sse->value, sse->len)) { return -1; } } sse = sse->next; } #ifdef UWSGI_ROUTING // before sending the file, we need to check if some rule applies if (!wsgi_req->is_routing && uwsgi_apply_routes_do(wsgi_req, NULL, 0) == UWSGI_ROUTE_BREAK) { return 0; } wsgi_req->routes_applied = 1; #endif return uwsgi_real_file_serve(wsgi_req, real_filename, real_filename_len, &st); } return -1; }
void uwsgi_detach_daemons() { struct uwsgi_daemon *ud = uwsgi.daemons; while (ud) { #ifdef UWSGI_SSL // stop any legion daemon, doesn't matter if dumb or smart if (ud->pid > 0 && (ud->legion || !ud->pidfile)) { #else // stop only dumb daemons if (ud->pid > 0 && !ud->pidfile) { #endif uwsgi_log("[uwsgi-daemons] stopping daemon (pid: %d): %s\n", (int) ud->pid, ud->command); // try to stop daemon gracefully, kill it if it won't die // if mercy is not set then wait up to 3 seconds time_t timeout = uwsgi_now() + (uwsgi.reload_mercy ? uwsgi.reload_mercy : 3); int waitpid_status; while (!kill(ud->pid, 0)) { kill(-ud->pid, ud->stop_signal); sleep(1); waitpid(-ud->pid, &waitpid_status, WNOHANG); if (uwsgi_now() >= timeout) { uwsgi_log("[uwsgi-daemons] daemon did not die in time, killing (pid: %d): %s\n", (int) ud->pid, ud->command); kill(-ud->pid, SIGKILL); break; } } // unregister daemon to prevent it from being respawned ud->registered = 0; } ud = ud->next; } } void uwsgi_spawn_daemon(struct uwsgi_daemon *ud) { // skip unregistered daemons if (!ud->registered) return; int throttle = 0; if (uwsgi.current_time - ud->last_spawn <= 3) { throttle = ud->respawns - (uwsgi.current_time - ud->last_spawn); // if ud->respawns == 0 then we can end up with throttle < 0 if (throttle <= 0) throttle = 1; } pid_t pid = uwsgi_fork("uWSGI external daemon"); if (pid < 0) { uwsgi_error("fork()"); return; } if (pid > 0) { ud->has_daemonized = 0; ud->pid = pid; ud->status = 1; ud->pidfile_checks = 0; if (ud->respawns == 0) { ud->born = uwsgi_now(); } ud->respawns++; ud->last_spawn = uwsgi_now(); } else { // close uwsgi sockets uwsgi_close_all_sockets(); uwsgi_close_all_fds(); if (ud->gid) { if (setgid(ud->gid)) { uwsgi_error("uwsgi_spawn_daemon()/setgid()"); exit(1); } } if (ud->uid) { if (setuid(ud->uid)) { uwsgi_error("uwsgi_spawn_daemon()/setuid()"); exit(1); } } if (ud->daemonize) { /* refork... */ pid = fork(); if (pid < 0) { uwsgi_error("fork()"); exit(1); } if (pid != 0) { _exit(0); } uwsgi_write_pidfile(ud->pidfile); } if (!uwsgi.daemons_honour_stdin && !ud->honour_stdin) { // /dev/null will became stdin uwsgi_remap_fd(0, "/dev/null"); } if (setsid() < 0) { uwsgi_error("setsid()"); exit(1); } if (!ud->pidfile) { #ifdef __linux__ if (prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0)) { uwsgi_error("prctl()"); } #endif } if (throttle) { uwsgi_log("[uwsgi-daemons] throttling \"%s\" for %d seconds\n", ud->command, throttle); sleep((unsigned int) throttle); } uwsgi_log("[uwsgi-daemons] %sspawning \"%s\" (uid: %d gid: %d)\n", ud->respawns > 0 ? "re" : "", ud->command, (int) getuid(), (int) getgid()); uwsgi_exec_command_with_args(ud->command); uwsgi_log("[uwsgi-daemons] unable to spawn \"%s\"\n", ud->command); // never here; exit(1); } return; } void uwsgi_opt_add_daemon(char *opt, char *value, void *none) { struct uwsgi_daemon *uwsgi_ud = uwsgi.daemons, *old_ud; char *pidfile = NULL; int daemonize = 0; int freq = 10; char *space = NULL; int stop_signal = SIGTERM; int reload_signal = 0; char *command = uwsgi_str(value); #ifdef UWSGI_SSL char *legion = NULL; if (!uwsgi_starts_with(opt, strlen(command), "legion-", 7)) { space = strchr(command, ' '); if (!space) { uwsgi_log("invalid legion daemon syntax: %s\n", command); exit(1); } *space = 0; legion = command; command = space+1; } #endif if (!strcmp(opt, "smart-attach-daemon") || !strcmp(opt, "smart-attach-daemon2") || !strcmp(opt, "legion-smart-attach-daemon") || !strcmp(opt, "legion-smart-attach-daemon2")) { space = strchr(command, ' '); if (!space) { uwsgi_log("invalid smart-attach-daemon syntax: %s\n", command); exit(1); } *space = 0; pidfile = command; // check for freq char *comma = strchr(pidfile, ','); if (comma) { *comma = 0; freq = atoi(comma + 1); } command = space + 1; if (!strcmp(opt, "smart-attach-daemon2") || !strcmp(opt, "legion-smart-attach-daemon2")) { daemonize = 1; } } if (!uwsgi_ud) { uwsgi.daemons = uwsgi_calloc(sizeof(struct uwsgi_daemon)); uwsgi_ud = uwsgi.daemons; } else { while (uwsgi_ud) { old_ud = uwsgi_ud; uwsgi_ud = uwsgi_ud->next; } uwsgi_ud = uwsgi_calloc(sizeof(struct uwsgi_daemon)); old_ud->next = uwsgi_ud; } uwsgi_ud->command = command; uwsgi_ud->pid = 0; uwsgi_ud->status = 0; uwsgi_ud->freq = freq; uwsgi_ud->registered = 0; uwsgi_ud->next = NULL; uwsgi_ud->respawns = 0; uwsgi_ud->last_spawn = 0; uwsgi_ud->daemonize = daemonize; uwsgi_ud->pidfile = pidfile; uwsgi_ud->control = 0; uwsgi_ud->stop_signal = stop_signal; uwsgi_ud->reload_signal = reload_signal; if (!strcmp(opt, "attach-control-daemon")) { uwsgi_ud->control = 1; } #ifdef UWSGI_SSL uwsgi_ud->legion = legion; #endif uwsgi.daemons_cnt++; } void uwsgi_opt_add_daemon2(char *opt, char *value, void *none) { struct uwsgi_daemon *uwsgi_ud = uwsgi.daemons, *old_ud; char *d_command = NULL; char *d_freq = NULL; char *d_pidfile = NULL; char *d_control = NULL; char *d_legion = NULL; char *d_daemonize = NULL; char *d_touch = NULL; char *d_stopsignal = NULL; char *d_reloadsignal = NULL; char *d_stdin = NULL; char *d_uid = NULL; char *d_gid = NULL; char *arg = uwsgi_str(value); if (uwsgi_kvlist_parse(arg, strlen(arg), ',', '=', "command", &d_command, "cmd", &d_command, "exec", &d_command, "freq", &d_freq, "pidfile", &d_pidfile, "control", &d_control, "daemonize", &d_daemonize, "daemon", &d_daemonize, "touch", &d_touch, "stopsignal", &d_stopsignal, "stop_signal", &d_stopsignal, "reloadsignal", &d_reloadsignal, "reload_signal", &d_reloadsignal, "stdin", &d_stdin, "uid", &d_uid, "gid", &d_gid, NULL)) { uwsgi_log("invalid --%s keyval syntax\n", opt); exit(1); } if (!d_command) { uwsgi_log("--%s: you need to specify a 'command' key\n", opt); exit(1); } #ifndef UWSGI_SSL if (d_legion) { uwsgi_log("legion subsystem is not supported on this uWSGI version, rebuild with ssl support\n"); exit(1); } #endif if (!uwsgi_ud) { uwsgi.daemons = uwsgi_calloc(sizeof(struct uwsgi_daemon)); uwsgi_ud = uwsgi.daemons; } else { while (uwsgi_ud) { old_ud = uwsgi_ud; uwsgi_ud = uwsgi_ud->next; } uwsgi_ud = uwsgi_calloc(sizeof(struct uwsgi_daemon)); old_ud->next = uwsgi_ud; } uwsgi_ud->command = d_command; uwsgi_ud->freq = d_freq ? atoi(d_freq) : 10; uwsgi_ud->daemonize = d_daemonize ? 1 : 0; uwsgi_ud->pidfile = d_pidfile; uwsgi_ud->stop_signal = d_stopsignal ? atoi(d_stopsignal) : SIGTERM; uwsgi_ud->reload_signal = d_reloadsignal ? atoi(d_reloadsignal) : 0; uwsgi_ud->control = d_control ? 1 : 0; uwsgi_ud->uid = d_uid ? atoi(d_uid) : 0; uwsgi_ud->gid = d_gid ? atoi(d_gid) : 0; uwsgi_ud->honour_stdin = d_stdin ? 1 : 0; #ifdef UWSGI_SSL uwsgi_ud->legion = d_legion; #endif if (d_touch) { size_t i,rlen = 0; char **argv = uwsgi_split_quoted(d_touch, strlen(d_touch), ";", &rlen); for(i=0;i<rlen;i++) { uwsgi_string_new_list(&uwsgi_ud->touch, argv[i]); } if (argv) free(argv); } uwsgi.daemons_cnt++; free(arg); }
static int uwsgi_cgi_request(struct wsgi_request *wsgi_req) { char full_path[PATH_MAX]; char tmp_path[PATH_MAX]; struct stat cgi_stat; int need_free = 0; int is_a_file = 0; int discard_base = 0; size_t docroot_len = 0; size_t full_path_len = 0; char *helper = NULL; char *path_info = NULL; char *script_name = NULL; /* Standard CGI request */ if (!wsgi_req->uh->pktsize) { uwsgi_log("Empty CGI request. skip.\n"); return -1; } if (uwsgi_parse_vars(wsgi_req)) { return -1; } char *docroot = NULL; // check for file availability (and 'runnability') if (uc.from_docroot) { docroot = wsgi_req->document_root; docroot_len = wsgi_req->document_root_len; } else { docroot = uwsgi_cgi_get_docroot(wsgi_req->path_info, wsgi_req->path_info_len, &need_free, &is_a_file, &discard_base, &script_name); if (docroot) docroot_len = strlen(docroot); } if (docroot == NULL || docroot_len == 0) { uwsgi_404(wsgi_req); return UWSGI_OK; } memcpy(full_path, docroot, docroot_len); if (!is_a_file) { *(full_path+docroot_len) = '/'; *(full_path+docroot_len+1) = 0; if (uwsgi_cgi_walk(wsgi_req, full_path, docroot, docroot_len, discard_base, &path_info)) { if (need_free) free(docroot); return UWSGI_OK; } if (realpath(full_path, tmp_path) == NULL) { if (need_free) free(docroot); uwsgi_404(wsgi_req); return UWSGI_OK; } full_path_len = strlen(tmp_path); // add +1 to copy the null byte memcpy(full_path, tmp_path, full_path_len+1); if (uwsgi_starts_with(full_path, full_path_len, docroot, docroot_len)) { if (need_free) free(docroot); uwsgi_log("CGI security error: %s is not under %s\n", full_path, docroot); return -1; } } else { *(full_path+docroot_len) = 0; path_info = wsgi_req->path_info+discard_base; } if (stat(full_path, &cgi_stat)) { uwsgi_404(wsgi_req); if (need_free) free(docroot); return UWSGI_OK; } if (S_ISDIR(cgi_stat.st_mode)) { // add / to directories if (wsgi_req->path_info_len == 0 || (wsgi_req->path_info_len > 0 && wsgi_req->path_info[wsgi_req->path_info_len-1] != '/')) { uwsgi_redirect_to_slash(wsgi_req); if (need_free) free(docroot); return UWSGI_OK; } struct uwsgi_string_list *ci = uc.index; full_path[full_path_len] = '/'; full_path_len++; int found = 0; while(ci) { if (full_path_len + ci->len + 1 < PATH_MAX) { // add + 1 to ensure null byte memcpy(full_path+full_path_len, ci->value, ci->len + 1); if (!access(full_path, R_OK)) { found = 1; break; } } ci = ci->next; } if (!found) { uwsgi_404(wsgi_req); if (need_free) free(docroot); return UWSGI_OK; } } full_path_len = strlen(full_path); int cgi_allowed = 1; struct uwsgi_string_list *allowed = uc.allowed_ext; while(allowed) { cgi_allowed = 0; if (full_path_len >= allowed->len) { if (!uwsgi_strncmp(full_path+(full_path_len-allowed->len), allowed->len, allowed->value, allowed->len)) { cgi_allowed = 1; break; } } allowed = allowed->next; } if (!cgi_allowed) { uwsgi_403(wsgi_req); if (need_free) free(docroot); return UWSGI_OK; } // get the helper if (!is_a_file) { helper = uwsgi_cgi_get_helper(full_path); if (helper == NULL) { if (access(full_path, X_OK)) { uwsgi_error("access()"); uwsgi_403(wsgi_req); if (need_free) free(docroot); return UWSGI_OK; } } } int ret = uwsgi_cgi_run(wsgi_req, docroot, docroot_len, full_path, helper, path_info, script_name, is_a_file, discard_base); if (need_free) free(docroot); return ret; }
static void stats_pusher_statsd(struct uwsgi_stats_pusher_instance *uspi, time_t now, char *json, size_t json_len) { if (!uspi->configured) { struct statsd_node *sn = uwsgi_calloc(sizeof(struct statsd_node)); char *comma = strchr(uspi->arg, ','); if (comma) { sn->prefix = comma+1; sn->prefix_len = strlen(sn->prefix); *comma = 0; } else { sn->prefix = "uwsgi"; sn->prefix_len = 5; } char *colon = strchr(uspi->arg, ':'); if (!colon) { uwsgi_log("invalid statsd address %s\n", uspi->arg); if (comma) *comma = ','; free(sn); return; } sn->addr_len = socket_to_in_addr(uspi->arg, colon, 0, &sn->addr.sa_in); sn->fd = socket(AF_INET, SOCK_DGRAM, 0); if (sn->fd < 0) { uwsgi_error("stats_pusher_statsd()/socket()"); if (comma) *comma = ','; free(sn); return; } uwsgi_socket_nb(sn->fd); if (comma) *comma = ','; uspi->data = sn; uspi->configured = 1; } // we use the same buffer for all of the packets struct uwsgi_buffer *ub = uwsgi_buffer_new(uwsgi.page_size); struct uwsgi_metric *um = uwsgi.metrics; while(um) { if (u_stats_pusher_statsd.no_workers && !uwsgi_starts_with(um->name, um->name_len, "worker.", 7)) { goto next; } uwsgi_rlock(uwsgi.metrics_lock); // ignore return value if (um->type == UWSGI_METRIC_GAUGE) { statsd_send_metric(ub, uspi, um->name, um->name_len, *um->value, "|g"); } else { statsd_send_metric(ub, uspi, um->name, um->name_len, *um->value, "|c"); } uwsgi_rwunlock(uwsgi.metrics_lock); if (um->reset_after_push){ uwsgi_wlock(uwsgi.metrics_lock); *um->value = um->initial_value; uwsgi_rwunlock(uwsgi.metrics_lock); } next: um = um->next; } uwsgi_buffer_destroy(ub); }